npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

scripting-tools

v0.19.16

Published

A toolbox that help scripting with Node.

Downloads

6,531

Readme

scripting-tools

A toolbox that help scripting with Node.

It is mainly a proxy to child_process.

/**
 * After this function is called every call to execSync
 * or exec will print the unix commands being executed.
 * */
export declare function enableCmdTrace(): void;
export declare function get_uid(unix_user: string): number;
export declare function get_gid(unix_user: string): number;
export declare function colorize(str: string, color: "GREEN" | "RED" | "YELLOW"): string;
/**
 *
 * The stderr is forwarded to the console realtime.
 *
 * The returned value is the concatenated data received on stdout.
 *
 * If the return code of the cmd is not 0 an exception is thrown
 * and the message cmd + the concatenated data received on stderr.
 *
 * If enableTrace() have been called the command called will be printed.
 *
 */
export declare function execSync(cmd: string, options?: child_process.ExecSyncOptions): string;
/**
 *
 * The cmd is printed before execution
 * stdout and stderr are forwarded to the console realtime.
 * Return nothing.
 *
 * stdio is set to "inherit" and thus should not be redefined.
 *
 */
export declare function execSyncTrace(cmd: string, options?: child_process.ExecSyncOptions): void;
/**
 *
 * Like execSync but stderr is not forwarded.
 * WARNING: If mean that when the cmd return 0
 * all data that may have been wrote on stderr
 * are lost into oblivion.
 *
 * stdio is set to "pipe" and thus should not be redefined.
 *
 */
export declare function execSyncQuiet(cmd: string, options?: child_process.ExecSyncOptions): string;
/** Same as execSync but async */
export declare function exec(cmd: string, options?: child_process.ExecOptions): Promise<string>;
/**
 * Spawn a process that continue running after current process exit.
 * This process will be ignored by stopSubProcessesAsapSync.
 * If a logfile_path if provided stdout and stderr will be redirected to this file.
 *
 * detached, and stdio options should not be set as they are set internally.
 * */
export declare function spawnAndDetach(command: string, args?: ReadonlyArray<string>, options?: child_process.SpawnOptions, logfile_path?: string): child_process.ChildProcess;
/**
 *
 * Print a message and enable a moving loading bar.
 * WARNING: Nothing should be printed to stdout until we stop showing the moving loading.
 *
 * returns:
 * -exec: A proxy to the exec fnc that will call onError before it throw the error.
 * -onSuccess: Stop showing the moving loading and pretty print a success message ("ok" by default)
 * -onError: Stop showing the moving loading and pretty print error message.
 *
 */
export declare function start_long_running_process(message: string): {
    exec: typeof exec;
    onSuccess(message?: string): void;
    onError(errorMessage: string): void;
};
/**
 * Apt package if not already installed,
 * if prog is provided and prog is in the PATH the package will not be installed
 * */
export declare function apt_get_install_if_missing(package_name: string, prog?: string): Promise<void>;
export declare namespace apt_get_install_if_missing {
    function isPkgInstalled(package_name: string): boolean;
    function doesHaveProg(prog: string): boolean;
}
/** Install or upgrade package via APT */
export declare function apt_get_install(package_name: string): Promise<void>;
export declare namespace apt_get_install {
    let isFirst: boolean;
    function record_installed_package(file_json_path: string, package_name: string): void;
    let onError: (error: Error) => never;
    let onInstallSuccess: (package_name: string) => void;
}
export declare function exit_if_not_root(): void;
/**
 *
 * Locate a given module in a node_modules directory.
 * If the module is required in different version and thus
 * present multiple times will be returned the shorter path.
 * This ensure that if a given module is in package.json 's dependencies
 * section the returned path will be the one we looking for.
 *
 * @param module_name The name of the module.
 * @param module_dir_path Path to the root of the module ( will search in ./node_modules ).
 *
 * Throw if the module is not found.
 *
 */
export declare function find_module_path(module_name: string, module_dir_path: string): string;
/**
 *
 * Test if two file of folder are same.
 * Does not consider stat ( ownership and permission ).
 * transparent handling of symlinks.
 *
 * Example
 *
 * /foo1/bar/file.txt
 * /foo2/bar/file.txt
 *
 * to compare the two version of file.txt
 * call with "/foo1", "/foo2", "./bar/file.txt";
 * or with "/foo1/bar/file.txt", "/foo2/bar/file.txt"
 *
 * @param relative_from_path1 absolute path ex: '/foo1'
 * @param relative_from_path2 absolute path ex: '/foo2'
 * @param relative_to_path relative path ex: './bar/file.txt" or 'bar/file.txt'
 * for convenience relative_to_path can be absolute as long as it has relative_from_path1
 * or relative_from_path2 as parent.
 *
 */
export declare function fs_areSame(relative_from_path1: string, relative_from_path2: string, relative_to_path?: string): boolean;
export declare namespace fs_areSame {
    function get_relative_to_path(dir_path1: string, dir_path2: string, to_path: string): string;
}
/**
 *
 * Move or copy file of folder.
 * -If dest is identical to source nothing is copied nor moved.
 * -If dest exist and is different of source it will be deleted prior to proceeding with action.
 * -In move mode if dest identical to source source will be removed.
 * -When copy is effectively performed the stat are conserved.
 * -If dirname of dest does not exist in fs, it will be created.
 * -Unlike cp or mv "/src/file.txt" "/dest" will NOT place file.txt in dest but dest will become file.txt
 *
 * calling [action] "/src/foo" "/dst/foo" is equivalent
 * to calling [action] "/src" "/dst" "./foo" ( or "foo" )
 * or [action] "/src" "/dst" "src/foo"
 * or [action] "/src" "/dst" "dst/foo"
 *
 */
export declare function fs_move(action: "COPY" | "MOVE", relative_from_path_src: string, relative_from_path_dest: string, relative_to_path?: string): void;
/**
 * Download and extract a tarball. throws web_get.DownloadError and Error
 *
 * Example
 *
 * website.com/rel.tar.gz
 * ./file1.txt
 * ./dir/file2.txt
 *
 * /foo/
 * ./file3.txt
 * ./dir/file4.txt
 *
 * calling with "website.com/rel.tar.gz", "MERGE" will result in:
 *
 * /foo/
 * ./file1.txt
 * ./file3.txt
 * ./dir/file4.txt
 *
 * calling with "website.com/rel.tar.gz", "OVERWRITE IF EXIST" will result in:
 *
 * /foo/
 * ./file1.txt
 * ./dir/file2.txt
 *
 */
export declare function download_and_extract_tarball(url: string, dest_dir_path: string, mode: "MERGE" | "OVERWRITE IF EXIST"): Promise<void>;
/** 10s of inactivity will trigger timeout, throws DownloadError only */
export declare function web_get(url: string, file_path: string): Promise<void>;
export declare function web_get(url: string): Promise<string>;
export declare namespace web_get {
    class DownloadError extends Error {
        readonly url: string;
        readonly cause: "CONNECTION ERROR" | "INCOMPLETE" | "HTTP ERROR CODE";
        constructor(url: string, cause: "CONNECTION ERROR" | "INCOMPLETE" | "HTTP ERROR CODE", message: string);
    }
    class DownloadErrorIncomplete extends DownloadError {
        readonly contentLength: number | undefined;
        readonly receivedBytes: number;
        constructor(url: string, contentLength: number | undefined, receivedBytes: number, info?: string);
    }
    class DownloadErrorHttpErrorCode extends DownloadError {
        readonly code: number;
        constructor(url: string, code: number);
    }
}
export declare function fs_ls(dir_path: string, mode?: "FILENAME" | "ABSOLUTE PATH", showHidden?: boolean): string[];
/**
 *
 * Create a symbolic link.
 * If dst exist it is removed.
 * directories leading to dest are created if necessary.
 *
 */
export declare function createSymlink(src_path: string, dst_path: string): void;
/** Create a executable file */
export declare function createScript(file_path: string, content: string): void;
export declare namespace unixUser {
    function create(unix_user: string, home_dir_path?: string): void;
    function remove(unix_user: string): void;
}
export { get_caller_file_path } from "./get_caller_file_path";
/**
 *
 * Equivalent to the pattern $() in bash.
 * Strip final LF if present.
 * If cmd fail no error is thrown, an empty string is returned.
 * Does not print to stdout.
 *
 * Typical usage: uname -r or which pkill
 *
 */
export declare function sh_eval(cmd: string): string;
/**
 * Run a command and return true if the return code was 0.
 * Does not print to stdout.
 */
export declare function sh_if(cmd: string): boolean;
/**
 * Return a promise that resolve as the source promise when fulfilled
 * or resolve with the error when reject.
 * If a timeout is specified the returned promise resolve with an error after [timeout]ms
 * if the source promise did not completed before.
 * The message of the timeout error is safePr.timeoutErrorMessage
 */
export declare function safePr<T>(pr: Promise<T>, timeout?: number): Promise<T | Error>;
export declare namespace safePr {
    const timeoutErrorMessage = "safePr timeout";
}
/**
 *
 * Allow to schedule action function to perform before exiting.
 *
 * The task function will always be called before the process stop
 * unless process.exit is explicitly called somewhere or
 * if the process receive any signal other than the ones specified
 * in the ExitCause.Signal["signal"] type.
 *
 * The process may stop for tree reasons:
 * 1) If there is no more work scheduled ( natural termination ).
 * 2) If an uncaught exception it thrown ( or a unhandled promise rejection )
 * 3) If a signal ( one of the handled ) is sent to the process.
 *
 * To manually exit the process there is two option:
 * - Call process.exit(X) but the task function will not be called.
 * - Emit "beforeExit" on process object ( process.emit("beforeExit, process.exitCode= X) );
 *  Doing so you simulate 1st stop condition ( natural termination ).
 *
 * To define the return code set process.exitCode. The exit code can be set
 * before emitting "beforeExit" or in the task function.
 * If exitCode has not be defined the process will exit with 0 if
 * there was nothing else to do and 1 otherwise.
 *
 * The task function can be synchronous or asynchronous.
 * The task function has [timeout] ms to complete.
 * If it has not completed within this delay the process will
 * be terminated anyway. (Default 4000 ms )
 * Setting [timeout] to a negative value will disable the timer.
 * WARNING: It is important not to perform sync operation that can
 * hang for a long time in the task function ( e.g. execSync("sleep 1000"); )
 * because while the sync operation are performed the timeout can't be triggered.
 *
 * As soon as the task function is called all the other exitCause that
 * may auccur will be ignored so that the task function have time to complete.
 * Anyway the task function is called only once.
 *
 * Whether the task function complete by successfully or throw
 * an exception the process will terminate with exit code set
 * in process.exitCode at the time of the completion.
 *
 * Provide shouldExitIf function to filter what should be
 * considered a case to terminate the process.
 * Only exception and supported signals can be bypassed,
 * Nothing else to do will always terminate the process.
 * By default exiting on any signal or uncaught errors.
 *
 * Before exiting all subprocess will be killed.
 *
 *
 */
export declare function setProcessExitHandler(task: (exitCause: setProcessExitHandler.ExitCause) => any, timeout?: number, shouldExitIf?: (exitCause: Exclude<setProcessExitHandler.ExitCause, setProcessExitHandler.ExitCause.NothingElseToDo>) => boolean): void;
export declare namespace setProcessExitHandler {
    type ExitCause = ExitCause.Signal | ExitCause.Exception | ExitCause.NothingElseToDo;
    namespace ExitCause {
        type Signal = {
            type: "SIGNAL";
            signal: keyof typeof Signal._obj;
        };
        namespace Signal {
            const _obj: {
                "SIGINT": null;
                "SIGUSR2": null;
                "SIGHUP": null;
            };
            const list: Signal["signal"][];
        }
        type Exception = {
            type: "EXCEPTION";
            error: Error;
        };
        type NothingElseToDo = {
            type: "NOTHING ELSE TO DO";
        };
    }
    let log: typeof console.log;
}
/**
 *
 * Stop a process by sending a specific signal to a target process.
 * When the function return the main process and all it's descendent processes are terminated.
 *
 * The default signal is SIGUSR2 which is the signal used to gracefully terminate
 * Process created by the createService function.
 *
 * Optionally runfiles_path can be provided to define a set of files
 * that should be suppressed before returning.
 *
 * If pid is provided under the form of a pidfile path it will
 * be added to the runfiles set.
 *
 * If all the processes does not terminate within [delay_before_sigkill]ms
 * (default 50000) then KILL signal will be sent to all processes still alive.
 *
 * If the PID provided is the same that the PID of the process running the function
 * PidMatchCurrentProcessError will be thrown.
 *
 */
export declare function stopProcessSync(pidfile_path_or_pid: string | number, signal?: NodeJS.Signals, delay_before_sigkill?: number, runfiles_path?: string[]): void;
export declare namespace stopProcessSync {
    class PidMatchCurrentProcessError extends Error {
        readonly cleanupRunfiles: () => void;
        constructor(cleanupRunfiles: () => void);
    }
    /**
     * Stopping process As Soon As Possible,
     * stopProcessSync with signal SIGKILL and timeout 0
     * */
    function stopProcessAsapSync(pidfile_path_or_pid: string | number, runfiles_path?: string[]): void;
    /**
     * Terminate all child process of current process ASAP.
     *
     * NOTE: Directly after this function ( in the current tick )
     * direct parents process that had sub processes will be Zombies.
     * However they will be reaped by the current process on next tick.
     *
     */
    function stopSubProcessesAsapSync(): void;
    namespace stopSubProcessesAsapSync {
        const ignorePids: Set<number>;
    }
    /** Invoke kill, can't throw */
    function kill(pid: number, signal: NodeJS.Signals): void;
    /**
     * Get the list of subprocess of a process ( return a list of pid )
     */
    function getSubProcesses(pid: number, depth: "FULL PROCESS TREE" | "DIRECT SUB PROCESSES ONLY"): number[];
    /** Return true only if exist and is not a daemon */
    function isProcessRunning(pid: number): boolean;
    /** Debug function to print the process tree of the current process. */
    function _printProcessTree(log?: typeof console.log): void;
    let log: typeof console.log;
}
/**
 *
 * Function to create the entry point (main.js) of a node service that can:
 * -Restart on crash (without relying on systemd to do so).
 * -Execute as specific unix user but can perform tasks as root before start.
 * -Be stopped gracefully by sending USR2 signal on the root process ( identified by pidfile ).
 * -Be started via a shell and gracefully stopped with CTRL-C (INT signal).
 * -Ensure only one instance of the service run at the same time.
 *      ( if at the time the main is called there is an other instance of the service
 *      running it is gracefully terminated )
 * -Ensure that the process will terminate in at most [ stop_timeout ] ms after
 *      receiving INT or USR2 signal. (default 5second)
 * -Forward daemon processes stdout to root process stdout.
 * -Can fork multiple daemon process.
 *
 * The root process forward command line arguments and environnement variable to
 * the daemon processes.
 *
 * => rootProcess function should return ( when not default ):
 * -pidfile_path: where to store the pid of the root process.
 *      take to terminate after requested to exit gracefully.
 * -srv_name: Name of the service to overwrite the process names. (Default: not overwriting)
 * -stop_timeout: The maximum amount of time ( in ms ) the
 *      that beforeExitTask can take to complete before being killed by force by root process.
 *      After receiving USR2 signal or CTRL, the root process will be closed within [trop_timeout]+1000ms
 * -assert_unix_user: enforce that the main be called by a specific user.
 * -isQuiet?: set to true to disable process debug info logging on stdout. Prefixed by [ service ]. ( default false )
 * -doForwardDaemonStdout?: set to true to forward everything the daemon
 *      process write to stdout to the root process stdout. ( default true )
 * -daemon_unix_user?: User who should own the daemon process.
 * -daemon_node_path?: Node.js executable that should be used to by the daemon process.
 * -daemon_cwd?: working directory of the daemon process.
 * -daemon_restart_after_crash_delay?: ( Default to 500ms. )Delay in ms before restarting the daemon
 *      after it terminate without being requested to. If set to a negative number the daemons
 *      will not be restarted after it terminate for the first time and :
 *      If all daemons process exited with 0 and there is no other daemon process the root process
 *      will end with a clean exit code.
 *      If any of the daemon exit with an unclean code the root process will be terminated with an error code
 *      even if there is some other daemon running.
 * -daemon_count: Number of instance of daemon process that should be forked, default 1.
 * -max_consecutive_restart: Number of time a daemon should be restarted after crashing right after start.
 *      (Default ~Infinity).
 * -preForkTask: Task to perform before forking a daemon process.
 *      It is called just before forking the daemon process. ( called again on every restart. )
 *      If the function is async the daemon will not be forked until the returned promise resolve.
 *      If the function throw exception root process will exit with code 1.
 *      (pidfile will be deleted)
 *      If the function is async and if it need to spawn child processes then
 *      an implementation for terminateSubProcess ( passed as reference ) should be provided so that
 *      if when called it kill all the child processes then resolve once they are terminated.
 *      The to which the promise resolve will be used as exit code for the root process.
 *      Note that terminateSubProcess should never be called, it is a OUT parameter.
 *      However if the implementation provided is just to send a SIGKILL to the forked processes
 *      then there is no need to provide an implementation as all the root process's sub processes tree
 *      will be killed before exiting anyway.
 *
 * => daemonProcess
 * It should return:
 * -launch: the function that the daemon process need to call to start the actual job that the service is meant to perform.
 * -beforeExitTask: function that should be called before the daemon process exit. ( e.g. creating crash report ).
 *      If the daemon process is terminating due to an error the error will be passed as argument.
 *      There is two scenario that will led to this function NOT being called:
 *      1)The daemon process receive KILL or other deadly signal that can't be overridden.
 *      2)The root process terminate.
 * daemon_number represent the instance index of the daemon among the total of [damon_count] process forked.
 * It can be user to use a different logfile for each daemon process instance.
 *
 * NOTE: If the root process receive a deadly signal other than INT, USR2 or HUP
 * ( e.g. KILL or STOP ) the root and daemon processes will immediately terminate without
 * executing beforeExit tasks or removing pidfile.
 *
 * NOTE: because setting listener on "message" and "disconnect" process event prevent the
 * thread from terminating naturally where is nothing more to do if you wish to manually
 * terminate the daemon process without termination being requested from the parent you can:
 *        1) emit "beforeExit" on process setting the desired exit code ( process.emit("beforeExit", process.exitCode= X);
 *        2) throw an exception.
 *
 */
export declare function createService(params: {
    rootProcess(): Promise<{
        pidfile_path: string;
        srv_name?: string;
        stop_timeout?: number;
        assert_unix_user?: string;
        isQuiet?: boolean;
        doForwardDaemonStdout?: boolean;
        daemon_unix_user?: string;
        daemon_node_path?: string;
        daemon_cwd?: string;
        daemon_restart_after_crash_delay?: number;
        daemon_count?: number;
        max_consecutive_restart?: number;
        preForkTask?: (terminateChildProcesses: {
            impl: () => Promise<void>;
        }, daemon_number: number) => Promise<void> | void;
    }>;
    daemonProcess(daemon_number: number, daemon_count: number): Promise<{
        launch: () => any;
        beforeExitTask?: (error: Error | undefined) => Promise<void> | void;
    }>;
}): void;

export declare namespace systemd {
    /**
     * Generate a systemd config file for a service created via "createService" function
     * enable by default, start by default.
     */
    function createConfigFile(srv_name: string, main_js_path: string, node_path?: string, enable?: "ENABLE" | false, start?: "START" | false): void;
    /** Remove config file disable and reload daemon, never throw, stop is false by default */
    function deleteConfigFile(srv_name: string, stop?: false | "STOP"): void;
}