code:
<picture>
	<source media="(prefers-color-scheme: dark)" srcset="media/logo_dark.svg">

</picture>

[![Coverage Status](https://codecov.io/gh/sindresorhus/execa/branch/main/graph/badge.svg)](https://codecov.io/gh/sindresorhus/execa)

> Process execution for humans

---

				Sindre's open source work is supported by the community

		Special thanks to:

			<picture>
				<source width="360" media="(prefers-color-scheme: dark)" srcset="https://sindresorhus.com/assets/thanks/transloadit-logo-dark.svg">
				<source width="360" media="(prefers-color-scheme: light)" srcset="https://sindresorhus.com/assets/thanks/transloadit-logo.svg">

			</picture>

---

## Why

This package improves [`child_process`](https://nodejs.org/api/child_process.html) methods with:

- [Promise interface](#execacommandcommand-options).
- [Scripts interface](#scripts-interface), like `zx`.
- Improved [Windows support](https://github.com/IndigoUnited/node-cross-spawn#why), including [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) binaries.
- Executes [locally installed binaries](#preferlocal) without `npx`.
- [Cleans up](#cleanup) child processes when the parent process ends.
- [Graceful termination](#optionsforcekillaftertimeout).
- Get [interleaved output](#all) from `stdout` and `stderr` similar to what is printed on the terminal.
- [Strips the final newline](#stripfinalnewline) from the output so you don't have to do `stdout.trim()`.
- Convenience methods to pipe processes' [input](#input) and [output](#redirect-output-to-a-file).
- Can specify file and arguments [as a single string](#execacommandcommand-options) without a shell.
- [Verbose mode](#verbose-mode) for debugging.
- More descriptive errors.
- Higher max buffer: 100 MB instead of 1 MB.

## Install

```sh
npm install execa
```

## Usage

### Promise interface

```js
import {execa} from 'execa';

const {stdout} = await execa('echo', ['unicorns']);
console.log(stdout);
//=> 'unicorns'
```

### Scripts interface

For more information about Execa scripts, please see [this page](docs/scripts.md).

#### Basic

```js
import {$} from 'execa';

const branch = await $`git branch --show-current`;
await $`dep deploy --branch=${branch}`;
```

#### Multiple arguments

```js
import {$} from 'execa';

const args = ['unicorns', '&', 'rainbows!'];
const {stdout} = await $`echo ${args}`;
console.log(stdout);
//=> 'unicorns & rainbows!'
```

#### With options

```js
import {$} from 'execa';

await $({stdio: 'inherit'})`echo unicorns`;
//=> 'unicorns'
```

#### Shared options

```js
import {$} from 'execa';

const $$ = $({stdio: 'inherit'});

await $$`echo unicorns`;
//=> 'unicorns'

await $$`echo rainbows`;
//=> 'rainbows'
```

#### Verbose mode

```sh
> node file.js
unicorns
rainbows

> NODE_DEBUG=execa node file.js
[16:50:03.305] echo unicorns
unicorns
[16:50:03.308] echo rainbows
rainbows
```

### Input/output

#### Redirect output to a file

```js
import {execa} from 'execa';

// Similar to `echo unicorns > stdout.txt` in Bash
await execa('echo', ['unicorns']).pipeStdout('stdout.txt');

// Similar to `echo unicorns 2> stdout.txt` in Bash
await execa('echo', ['unicorns']).pipeStderr('stderr.txt');

// Similar to `echo unicorns &> stdout.txt` in Bash
await execa('echo', ['unicorns'], {all: true}).pipeAll('all.txt');
```

#### Redirect input from a file

```js
import {execa} from 'execa';

// Similar to `cat < stdin.txt` in Bash
const {stdout} = await execa('cat', {inputFile: 'stdin.txt'});
console.log(stdout);
//=> 'unicorns'
```

#### Save and pipe output from a child process

```js
import {execa} from 'execa';

const {stdout} = await execa('echo', ['unicorns']).pipeStdout(process.stdout);
// Prints `unicorns`
console.log(stdout);
// Also returns 'unicorns'
```

#### Pipe multiple processes

```js
import {execa} from 'execa';

// Similar to `echo unicorns | cat` in Bash
const {stdout} = await execa('echo', ['unicorns']).pipeStdout(execa('cat'));
console.log(stdout);
//=> 'unicorns'
```

### Handling Errors

```js
import {execa} from 'execa';

// Catching an error
try {
	await execa('unknown', ['command']);
} catch (error) {
	console.log(error);
	/*
	{
		message: 'Command failed with ENOENT: unknown command spawn unknown ENOENT',
		errno: -2,
		code: 'ENOENT',
		syscall: 'spawn unknown',
		path: 'unknown',
		spawnargs: ['command'],
		originalMessage: 'spawn unknown ENOENT',
		shortMessage: 'Command failed with ENOENT: unknown command spawn unknown ENOENT',
		command: 'unknown command',
		escapedCommand: 'unknown command',
		stdout: '',
		stderr: '',
		failed: true,
		timedOut: false,
		isCanceled: false,
		killed: false
	}
	*/
}
```

### Graceful termination

Using SIGTERM, and after 2 seconds, kill it with SIGKILL.

```js
const subprocess = execa('node');

setTimeout(() => {
	subprocess.kill('SIGTERM', {
		forceKillAfterTimeout: 2000
	});
}, 1000);
```

## API

### Methods

#### execa(file, arguments?, options?)

Executes a command using `file ...arguments`. `arguments` are specified as an array of strings. Returns a [`childProcess`](#childprocess).

Arguments are [automatically escaped](#shell-syntax). They can contain any character, including spaces.

This is the preferred method when executing single commands.

#### execaNode(scriptPath, arguments?, options?)

Executes a Node.js file using `node scriptPath ...arguments`. `arguments` are specified as an array of strings. Returns a [`childProcess`](#childprocess).

Arguments are [automatically escaped](#shell-syntax). They can contain any character, including spaces.

This is the preferred method when executing Node.js files.

Like [`child_process#fork()`](https://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options):
  - the current Node version and options are used. This can be overridden using the [`nodePath`](#nodepath-for-node-only) and [`nodeOptions`](#nodeoptions-for-node-only) options.
  - the [`shell`](#shell) option cannot be used
  - an extra channel [`ipc`](https://nodejs.org/api/child_process.html#child_process_options_stdio) is passed to [`stdio`](#stdio)

#### $\`command\`

Executes a command. The `command` string includes both the `file` and its `arguments`. Returns a [`childProcess`](#childprocess).

Arguments are [automatically escaped](#shell-syntax). They can contain any character, but spaces must use `${}` like `` $`echo ${'has space'}` ``.

This is the preferred method when executing multiple commands in a script file.

The `command` string can inject any `${value}` with the following types: string, number, [`childProcess`](#childprocess) or an array of those types. For example: `` $`echo one ${'two'} ${3} ${['four', 'five']}` ``. For `${childProcess}`, the process's `stdout` is used.

For more information, please see [this section](#scripts-interface) and [this page](docs/scripts.md).

#### $(options)

Returns a new instance of [`$`](#command) but with different default `options`. Consecutive calls are merged to previous ones.

This can be used to either:
  - Set options for a specific command: `` $(options)`command` ``
  - Share options for multiple commands: `` const $$ = $(options); $$`command`; $$`otherCommand`; ``

#### execaCommand(command, options?)

Executes a command. The `command` string includes both the `file` and its `arguments`. Returns a [`childProcess`](#childprocess).

Arguments are [automatically escaped](#shell-syntax). They can contain any character, but spaces must be escaped with a backslash like `execaCommand('echo has\\ space')`.

This is the preferred method when executing a user-supplied `command` string, such as in a REPL.

### execaSync(file, arguments?, options?)

Same as [`execa()`](#execacommandcommand-options) but synchronous.

Returns or throws a [`childProcessResult`](#childProcessResult).

### $.sync\`command\`

Same as [$\`command\`](#command) but synchronous.

Returns or throws a [`childProcessResult`](#childProcessResult).

### execaCommandSync(command, options?)

Same as [`execaCommand()`](#execacommand-command-options) but synchronous.

Returns or throws a [`childProcessResult`](#childProcessResult).

### Shell syntax

For all the [methods above](#methods), no shell interpreter (Bash, cmd.exe, etc.) is used unless the [`shell` option](#shell) is set. This means shell-specific characters and expressions (`$variable`, `&&`, `||`, `;`, `|`, etc.) have no special meaning and do not need to be escaped.

### childProcess

The return value of all [asynchronous methods](#methods) is both:
  - a `Promise` resolving or rejecting with a [`childProcessResult`](#childProcessResult).
  - a [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess) with the following additional methods and properties.

#### kill(signal?, options?)

Same as the original [`child_process#kill()`](https://nodejs.org/api/child_process.html#child_process_subprocess_kill_signal) except: if `signal` is `SIGTERM` (the default value) and the child process is not terminated after 5 seconds, force it by sending `SIGKILL`.

Note that this graceful termination does not work on Windows, because Windows [doesn't support signals](https://nodejs.org/api/process.html#process_signal_events) (`SIGKILL` and `SIGTERM` has the same effect of force-killing the process immediately.) If you want to achieve graceful termination on Windows, you have to use other means, such as [`taskkill`](https://github.com/sindresorhus/taskkill).

##### options.forceKillAfterTimeout

Type: `number | false`\
Default: `5000`

Milliseconds to wait for the child process to terminate before sending `SIGKILL`.

Can be disabled with `false`.

#### all

Type: `ReadableStream | undefined`

Stream combining/interleaving [`stdout`](https://nodejs.org/api/child_process.html#child_process_subprocess_stdout) and [`stderr`](https://nodejs.org/api/child_process.html#child_process_subprocess_stderr).

This is `undefined` if either:
  - the [`all` option](#all-2) is `false` (the default value)
  - both [`stdout`](#stdout-1) and [`stderr`](#stderr-1) options are set to [`'inherit'`, `'ipc'`, `Stream` or `integer`](https://nodejs.org/dist/latest-v6.x/docs/api/child_process.html#child_process_options_stdio)

#### pipeStdout(target)

[Pipe](https://nodejs.org/api/stream.html#readablepipedestination-options) the child process's `stdout` to `target`, which can be:
  - Another [`execa()` return value](#pipe-multiple-processes)
  - A [writable stream](#save-and-pipe-output-from-a-child-process)
  - A [file path string](#redirect-output-to-a-file)

If the `target` is another [`execa()` return value](#execacommandcommand-options), it is returned. Otherwise, the original `execa()` return value is returned. This allows chaining `pipeStdout()` then `await`ing the [final result](#childprocessresult).

The [`stdout` option](#stdout-1) must be kept as `pipe`, its default value.

#### pipeStderr(target)

Like [`pipeStdout()`](#pipestdouttarget) but piping the child process's `stderr` instead.

The [`stderr` option](#stderr-1) must be kept as `pipe`, its default value.

#### pipeAll(target)

Combines both [`pipeStdout()`](#pipestdouttarget) and [`pipeStderr()`](#pipestderrtarget).

Either the [`stdout` option](#stdout-1) or the [`stderr` option](#stderr-1) must be kept as `pipe`, their default value. Also, the [`all` option](#all-2) must be set to `true`.

### childProcessResult

Type: `object`

Result of a child process execution. On success this is a plain object. On failure this is also an `Error` instance.

The child process [fails](#failed) when:
- its [exit code](#exitcode) is not `0`
- it was [killed](#killed) with a [signal](#signal)
- [timing out](#timedout)
- [being canceled](#iscanceled)
- there's not enough memory or there are already too many child processes

#### command

Type: `string`

The file and arguments that were run, for logging purposes.

This is not escaped and should not be executed directly as a process, including using [`execa()`](#execafile-arguments-options) or [`execaCommand()`](#execacommandcommand-options).

#### escapedCommand

Type: `string`

Same as [`command`](#command-1) but escaped.

This is meant to be copy and pasted into a shell, for debugging purposes.
Since the escaping is fairly basic, this should not be executed directly as a process, including using [`execa()`](#execafile-arguments-options) or [`execaCommand()`](#execacommandcommand-options).

#### exitCode

Type: `number`

The numeric exit code of the process that was run.

#### stdout

Type: `string | Buffer`

The output of the process on stdout.

#### stderr

Type: `string | Buffer`

The output of the process on stderr.

#### all

Type: `string | Buffer | undefined`

The output of the process with `stdout` and `stderr` interleaved.

This is `undefined` if either:
  - the [`all` option](#all-2) is `false` (the default value)
  - `execaSync()` was used

#### failed

Type: `boolean`

Whether the process failed to run.

#### timedOut

Type: `boolean`

Whether the process timed out.

#### isCanceled

Type: `boolean`

Whether the process was canceled.

You can cancel the spawned process using the [`signal`](#signal-1) option.

#### killed

Type: `boolean`

Whether the process was killed.

#### signal

Type: `string | undefined`

The name of the signal that was used to terminate the process. For example, `SIGFPE`.

If a signal terminated the process, this property is defined and included in the error message. Otherwise it is `undefined`.

#### signalDescription

Type: `string | undefined`

A human-friendly description of the signal that was used to terminate the process. For example, `Floating point arithmetic error`.

If a signal terminated the process, this property is defined and included in the error message. Otherwise it is `undefined`. It is also `undefined` when the signal is very uncommon which should seldomly happen.

#### cwd

Type: `string`

The `cwd` of the command if provided in the [command options](#cwd-1). Otherwise it is `process.cwd()`.

#### message

Type: `string`

Error message when the child process failed to run. In addition to the [underlying error message](#originalMessage), it also contains some information related to why the child process errored.

The child process [stderr](#stderr) then [stdout](#stdout) are appended to the end, separated with newlines and not interleaved.

#### shortMessage

Type: `string`

This is the same as the [`message` property](#message) except it does not include the child process stdout/stderr.

#### originalMessage

Type: `string | undefined`

Original error message. This is the same as the `message` property except it includes neither the child process stdout/stderr nor some additional information added by Execa.

This is `undefined` unless the child process exited due to an `error` event or a timeout.

### options

Type: `object`

#### cleanup

Type: `boolean`\
Default: `true`

Kill the spawned process when the parent process exits unless either:
	- the spawned process is [`detached`](https://nodejs.org/api/child_process.html#child_process_options_detached)
	- the parent process is terminated abruptly, for example, with `SIGKILL` as opposed to `SIGTERM` or a normal exit

#### preferLocal

Type: `boolean`\
Default: `true` with [`$`](#command), `false` otherwise

Prefer locally installed binaries when looking for a binary to execute.\
If you `$ npm install foo`, you can then `execa('foo')`.

#### localDir

Type: `string | URL`\
Default: `process.cwd()`

Preferred path to find locally installed binaries in (use with `preferLocal`).

#### execPath

Type: `string`\
Default: `process.execPath` (Current Node.js executable)

Path to the Node.js executable to use in child processes.

This can be either an absolute path or a path relative to the [`cwd` option](#cwd).

Requires [`preferLocal`](#preferlocal) to be `true`.

For example, this can be used together with [`get-node`](https://github.com/ehmicky/get-node) to run a specific Node.js version in a child process.

#### buffer

Type: `boolean`\
Default: `true`

Buffer the output from the spawned process. When set to `false`, you must read the output of [`stdout`](#stdout-1) and [`stderr`](#stderr-1) (or [`all`](#all) if the [`all`](#all-2) option is `true`). Otherwise the returned promise will not be resolved/rejected.

If the spawned process fails, [`error.stdout`](#stdout), [`error.stderr`](#stderr), and [`error.all`](#all) will contain the buffered data.

#### input

Type: `string | Buffer | stream.Readable`

Write some input to the `stdin` of your binary.\
Streams are not allowed when using the synchronous methods.

If the input is a file, use the [`inputFile` option](#inputfile) instead.

#### inputFile

Type: `string`

Use a file as input to the the `stdin` of your binary.

If the input is not a file, use the [`input` option](#input) instead.

#### stdin

Type: `string | number | Stream | undefined`\
Default: `inherit` with [`$`](#command), `pipe` otherwise

Same options as [`stdio`](https://nodejs.org/dist/latest-v6.x/docs/api/child_process.html#child_process_options_stdio).

#### stdout

Type: `string | number | Stream | undefined`\
Default: `pipe`

Same options as [`stdio`](https://nodejs.org/dist/latest-v6.x/docs/api/child_process.html#child_process_options_stdio).

#### stderr

Type: `string | number | Stream | undefined`\
Default: `pipe`

Same options as [`stdio`](https://nodejs.org/dist/latest-v6.x/docs/api/child_process.html#child_process_options_stdio).

#### all

Type: `boolean`\
Default: `false`

Add an `.all` property on the [promise](#all) and the [resolved value](#all-1). The property contains the output of the process with `stdout` and `stderr` interleaved.

#### reject

Type: `boolean`\
Default: `true`

Setting this to `false` resolves the promise with the error instead of rejecting it.

#### stripFinalNewline

Type: `boolean`\
Default: `true`

Strip the final [newline character](https://en.wikipedia.org/wiki/Newline) from the output.

#### extendEnv

Type: `boolean`\
Default: `true`

Set to `false` if you don't want to extend the environment variables when providing the `env` property.

---

Execa also accepts the below options which are the same as the options for [`child_process#spawn()`](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options)/[`child_process#exec()`](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback)

#### cwd

Type: `string | URL`\
Default: `process.cwd()`

Current working directory of the child process.

#### env

Type: `object`\
Default: `process.env`

Environment key-value pairs. Extends automatically from `process.env`. Set [`extendEnv`](#extendenv) to `false` if you don't want this.

#### argv0

Type: `string`

Explicitly set the value of `argv[0]` sent to the child process. This will be set to `file` if not specified.

#### stdio

Type: `string | string[]`\
Default: `pipe`

Child's [stdio](https://nodejs.org/api/child_process.html#child_process_options_stdio) configuration.

#### serialization

Type: `string`\
Default: `'json'`

Specify the kind of serialization used for sending messages between processes when using the [`stdio: 'ipc'`](#stdio) option or [`execaNode()`](#execanodescriptpath-arguments-options):
	- `json`: Uses `JSON.stringify()` and `JSON.parse()`.
	- `advanced`: Uses [`v8.serialize()`](https://nodejs.org/api/v8.html#v8_v8_serialize_value)

[More info.](https://nodejs.org/api/child_process.html#child_process_advanced_serialization)

#### detached

Type: `boolean`

Prepare child to run independently of its parent process. Specific behavior [depends on the platform](https://nodejs.org/api/child_process.html#child_process_options_detached).

#### uid

Type: `number`

Sets the user identity of the process.

#### gid

Type: `number`

Sets the group identity of the process.

#### shell

Type: `boolean | string`\
Default: `false`

If `true`, runs `file` inside of a shell. Uses `/bin/sh` on UNIX and `cmd.exe` on Windows. A different shell can be specified as a string. The shell should understand the `-c` switch on UNIX or `/d /s /c` on Windows.

We recommend against using this option since it is:
- not cross-platform, encouraging shell-specific syntax.
- slower, because of the additional shell interpretation.
- unsafe, potentially allowing command injection.

#### encoding

Type: `string | null`\
Default: `utf8`

Specify the character encoding used to decode the `stdout` and `stderr` output. If set to `'buffer'` or `null`, then `stdout` and `stderr` will be a `Buffer` instead of a string.

#### timeout

Type: `number`\
Default: `0`

If timeout is greater than `0`, the parent will send the signal identified by the `killSignal` property (the default is `SIGTERM`) if the child runs longer than timeout milliseconds.

#### maxBuffer

Type: `number`\
Default: `100_000_000` (100 MB)

Largest amount of data in bytes allowed on `stdout` or `stderr`.

#### killSignal

Type: `string | number`\
Default: `SIGTERM`

Signal value to be used when the spawned process will be killed.

#### signal

Type: [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)

You can abort the spawned process using [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).

When `AbortController.abort()` is called, [`.isCanceled`](#iscanceled) becomes `true`.

#### windowsVerbatimArguments

Type: `boolean`\
Default: `false`

If `true`, no quoting or escaping of arguments is done on Windows. Ignored on other platforms. This is set to `true` automatically when the `shell` option is `true`.

#### windowsHide

Type: `boolean`\
Default: `true`

On Windows, do not create a new console window. Please note this also prevents `CTRL-C` [from working](https://github.com/nodejs/node/issues/29837) on Windows.

#### verbose

Type: `boolean`\
Default: `false`

[Print each command](#verbose-mode) on `stderr` before executing it.

This can also be enabled by setting the `NODE_DEBUG=execa` environment variable in the current process.

#### nodePath *(For `.node()` only)*

Type: `string`\
Default: [`process.execPath`](https://nodejs.org/api/process.html#process_process_execpath)

Node.js executable used to create the child process.

#### nodeOptions *(For `.node()` only)*

Type: `string[]`\
Default: [`process.execArgv`](https://nodejs.org/api/process.html#process_process_execargv)

List of [CLI options](https://nodejs.org/api/cli.html#cli_options) passed to the Node.js executable.

## Tips

### Retry on error

Gracefully handle failures by using automatic retries and exponential backoff with the [`p-retry`](https://github.com/sindresorhus/p-retry) package:

```js
import pRetry from 'p-retry';

const run = async () => {
	const results = await execa('curl', ['-sSL', 'https://sindresorhus.com/unicorn']);
	return results;
};

console.log(await pRetry(run, {retries: 5}));
```

### Cancelling a spawned process

```js
import {execa} from 'execa';

const abortController = new AbortController();
const subprocess = execa('node', [], {signal: abortController.signal});

setTimeout(() => {
	abortController.abort();
}, 1000);

try {
	await subprocess;
} catch (error) {
	console.log(subprocess.killed); // true
	console.log(error.isCanceled); // true
}
```

### Execute the current package's binary

```js
import {getBinPath} from 'get-bin-path';

const binPath = await getBinPath();
await execa(binPath);
```

`execa` can be combined with [`get-bin-path`](https://github.com/ehmicky/get-bin-path) to test the current package's binary. As opposed to hard-coding the path to the binary, this validates that the `package.json` `bin` field is correctly set up.

## Related

- [gulp-execa](https://github.com/ehmicky/gulp-execa) - Gulp plugin for `execa`
- [nvexeca](https://github.com/ehmicky/nvexeca) - Run `execa` using any Node.js version
- [sudo-prompt](https://github.com/jorangreef/sudo-prompt) - Run commands with elevated privileges.

## Maintainers

- [Sindre Sorhus](https://github.com/sindresorhus)
- [@ehmicky](https://github.com/ehmicky)

---

		Get professional support for this package with a Tidelift subscription

		Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies.


get-stream

description: Get a stream as a string, Buffer, ArrayBuffer or array

license: MIT

repository: sindresorhus/get-stream

type: module

benchmark: node benchmarks/index.js

test: xo && ava && tsd --typings=source/index.d.ts --files=source/index.test-d.ts

keywords: get stream promise concat string text buffer read data consume readable readablestream object concat

license: mit


code:
# get-stream

> Get a stream as a string, Buffer, ArrayBuffer or array

## Features

- Works in any JavaScript environment ([Node.js](#nodejs-streams), [browsers](#web-streams), etc.).
- Supports [text streams](#getstreamstream-options), [binary streams](#getstreamasbufferstream-options) and [object streams](#getstreamasarraystream-options).
- Supports [async iterables](#async-iterables).
- Can set a [maximum stream size](#maxbuffer).
- Returns [partially read data](#errors) when the stream errors.
- [Fast](#benchmarks).

## Install

```sh
npm install get-stream
```

## Usage

### Node.js streams

```js
import fs from 'node:fs';
import getStream from 'get-stream';

const stream = fs.createReadStream('unicorn.txt');

console.log(await getStream(stream));
/*
              ,,))))))));,
           __)))))))))))))),
\|/       -\(((((''''((((((((.
-*-==//////((''  .     `)))))),
/|\      ))| o    ;-.    '(((((                                  ,(,
         ( `|    /  )    ;))))'                               ,_))^;(~
            |   |   |   ,))((((_     _____------~~~-.        %,;(;(>';'~
            o_);   ;    )))(((` ~---~  `::           \      %%~~)(v;(`('~
                  ;    ''''````         `:       `:::|\,__,%%    );`'; ~
                 |   _                )     /      `:|`----'     `-'
           ______/\/~    |                 /        /
         /~;;.____/;;'  /          ___--,-(   `;;;/
        / //  _;______;'------~~~~~    /;;/\    /
       //  | |                        / ;   \;;,\
      (<_  | ;                      /',/-----'  _>
       \_| ||_                     //~;~~~~~~~~~
           `\_|                   (,~~
                                   \~\
                                    ~~
*/
```

### Web streams

```js
import getStream from 'get-stream';

const {body: readableStream} = await fetch('https://example.com');
console.log(await getStream(readableStream));
```

### Async iterables

```js
import {opendir} from 'node:fs/promises';
import {getStreamAsArray} from 'get-stream';

const asyncIterable = await opendir(directory);
console.log(await getStreamAsArray(asyncIterable));
```

## API

The following methods read the stream's contents and return it as a promise.

### getStream(stream, options?)

`stream`: [`stream.Readable`](https://nodejs.org/api/stream.html#class-streamreadable), [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream), or [`AsyncIterable<string | Buffer | ArrayBuffer | DataView | TypedArray>`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols)\
`options`: [`Options`](#options)

Get the given `stream` as a string.

### getStreamAsBuffer(stream, options?)

Get the given `stream` as a Node.js [`Buffer`](https://nodejs.org/api/buffer.html#class-buffer).

```js
import {getStreamAsBuffer} from 'get-stream';

const stream = fs.createReadStream('unicorn.png');
console.log(await getStreamAsBuffer(stream));
```

### getStreamAsArrayBuffer(stream, options?)

Get the given `stream` as an [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer).

```js
import {getStreamAsArrayBuffer} from 'get-stream';

const {body: readableStream} = await fetch('https://example.com');
console.log(await getStreamAsArrayBuffer(readableStream));
```

### getStreamAsArray(stream, options?)

Get the given `stream` as an array. Unlike [other methods](#api), this supports [streams of objects](https://nodejs.org/api/stream.html#object-mode).

```js
import {getStreamAsArray} from 'get-stream';

const {body: readableStream} = await fetch('https://example.com');
console.log(await getStreamAsArray(readableStream));
```

#### options

Type: `object`

##### maxBuffer

Type: `number`\
Default: `Infinity`

Maximum length of the stream. If exceeded, the promise will be rejected with a `MaxBufferError`.

Depending on the [method](#api), the length is measured with [`string.length`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length), [`buffer.length`](https://nodejs.org/api/buffer.html#buflength), [`arrayBuffer.byteLength`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/byteLength) or [`array.length`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length).

## Errors

If the stream errors, the returned promise will be rejected with the `error`. Any contents already read from the stream will be set to `error.bufferedData`, which is a `string`, a `Buffer`, an `ArrayBuffer` or an array depending on the [method used](#api).

```js
import getStream from 'get-stream';

try {
	await getStream(streamThatErrorsAtTheEnd('unicorn'));
} catch (error) {
	console.log(error.bufferedData);
	//=> 'unicorn'
}
```

## Tips

### Alternatives

If you do not need the [`maxBuffer`](#maxbuffer) option, [`error.bufferedData`](#errors), nor browser support, you can use the following methods instead of this package.

#### [`streamConsumers.text()`](https://nodejs.org/api/webstreams.html#streamconsumerstextstream)

```js
import fs from 'node:fs';
import {text} from 'node:stream/consumers';

const stream = fs.createReadStream('unicorn.txt', {encoding: 'utf8'});
console.log(await text(stream))
```

#### [`streamConsumers.buffer()`](https://nodejs.org/api/webstreams.html#streamconsumersbufferstream)

```js
import {buffer} from 'node:stream/consumers';

console.log(await buffer(stream))
```

#### [`streamConsumers.arrayBuffer()`](https://nodejs.org/api/webstreams.html#streamconsumersarraybufferstream)

```js
import {arrayBuffer} from 'node:stream/consumers';

console.log(await arrayBuffer(stream))
```

#### [`readable.toArray()`](https://nodejs.org/api/stream.html#readabletoarrayoptions)

```js
console.log(await stream.toArray())
```

#### [`Array.fromAsync()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fromAsync)

If your [environment supports it](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fromAsync#browser_compatibility):

```js
console.log(await Array.fromAsync(stream))
```

### Non-UTF-8 encoding

When all of the following conditions apply:
  - [`getStream()`](#getstreamstream-options) is used (as opposed to [`getStreamAsBuffer()`](#getstreamasbufferstream-options) or [`getStreamAsArrayBuffer()`](#getstreamasarraybufferstream-options))
  - The stream is binary (not text)
  - The stream's encoding is not UTF-8 (for example, it is UTF-16, hexadecimal, or Base64)

Then the stream must be decoded using a transform stream like [`TextDecoderStream`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoderStream) or [`b64`](https://github.com/hapijs/b64).

```js
import getStream from 'get-stream';

const textDecoderStream = new TextDecoderStream('utf-16le');
const {body: readableStream} = await fetch('https://example.com');
console.log(await getStream(readableStream.pipeThrough(textDecoderStream)));
```

### Blobs

[`getStreamAsArrayBuffer()`](#getstreamasarraybufferstream-options) can be used to create [Blobs](https://developer.mozilla.org/en-US/docs/Web/API/Blob).

```js
import {getStreamAsArrayBuffer} from 'get-stream';

const stream = fs.createReadStream('unicorn.txt');
console.log(new Blob([await getStreamAsArrayBuffer(stream)]));
```

### JSON streaming

[`getStreamAsArray()`](#getstreamasarraystream-options) can be combined with JSON streaming utilities to parse JSON incrementally.

```js
import fs from 'node:fs';
import {compose as composeStreams} from 'node:stream';
import {getStreamAsArray} from 'get-stream';
import streamJson from 'stream-json';
import streamJsonArray from 'stream-json/streamers/StreamArray.js';

const stream = fs.createReadStream('big-array-of-objects.json');
console.log(await getStreamAsArray(
	composeStreams(stream, streamJson.parser(), streamJsonArray.streamArray()),
));
```

## Benchmarks

### Node.js stream (100 MB, binary)

- `getStream()`: 142ms
- `text()`: 139ms
- `getStreamAsBuffer()`: 106ms
- `buffer()`: 83ms
- `getStreamAsArrayBuffer()`: 105ms
- `arrayBuffer()`: 81ms
- `getStreamAsArray()`: 24ms
- `stream.toArray()`: 21ms

### Node.js stream (100 MB, text)

- `getStream()`: 90ms
- `text()`: 89ms
- `getStreamAsBuffer()`: 127ms
- `buffer()`: 192ms
- `getStreamAsArrayBuffer()`: 129ms
- `arrayBuffer()`: 195ms
- `getStreamAsArray()`: 89ms
- `stream.toArray()`: 90ms

### Web ReadableStream (100 MB, binary)

- `getStream()`: 223ms
- `text()`: 221ms
- `getStreamAsBuffer()`: 182ms
- `buffer()`: 153ms
- `getStreamAsArrayBuffer()`: 171ms
- `arrayBuffer()`: 155ms
- `getStreamAsArray()`: 83ms

### Web ReadableStream (100 MB, text)

- `getStream()`: 141ms
- `text()`: 139ms
- `getStreamAsBuffer()`: 91ms
- `buffer()`: 80ms
- `getStreamAsArrayBuffer()`: 89ms
- `arrayBuffer()`: 81ms
- `getStreamAsArray()`: 21ms

[Benchmarks' source file](benchmarks/index.js).

## FAQ

### How is this different from [`concat-stream`](https://github.com/maxogden/concat-stream)?

This module accepts a stream instead of being one and returns a promise instead of using a callback. The API is simpler and it only supports returning a string, `Buffer`, an `ArrayBuffer` or an array. It doesn't have a fragile type inference. You explicitly choose what you want. And it doesn't depend on the huge `readable-stream` package.

## Related

- [get-stdin](https://github.com/sindresorhus/get-stdin) - Get stdin as a string or buffer
- [into-stream](https://github.com/sindresorhus/into-stream) - The opposite of this package


code:
import {getStreamContents} from './contents.js';
import {noop, throwObjectStream, getLengthProp} from './utils.js';

export async function getStreamAsArrayBuffer(stream, options) {
	return getStreamContents(stream, arrayBufferMethods, options);
}

const initArrayBuffer = () => ({contents: new ArrayBuffer(0)});

const useTextEncoder = chunk => textEncoder.encode(chunk);
const textEncoder = new TextEncoder();

const useUint8Array = chunk => new Uint8Array(chunk);

const useUint8ArrayWithOffset = chunk => new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);

const truncateArrayBufferChunk = (convertedChunk, chunkSize) => convertedChunk.slice(0, chunkSize);

// `contents` is an increasingly growing `Uint8Array`.
const addArrayBufferChunk = (convertedChunk, {contents, length: previousLength}, length) => {
	const newContents = hasArrayBufferResize() ? resizeArrayBuffer(contents, length) : resizeArrayBufferSlow(contents, length);
	new Uint8Array(newContents).set(convertedChunk, previousLength);
	return newContents;
};

// Without `ArrayBuffer.resize()`, `contents` size is always a power of 2.
// This means its last bytes are zeroes (not stream data), which need to be
// trimmed at the end with `ArrayBuffer.slice()`.
const resizeArrayBufferSlow = (contents, length) => {
	if (length <= contents.byteLength) {
		return contents;
	}

	const arrayBuffer = new ArrayBuffer(getNewContentsLength(length));
	new Uint8Array(arrayBuffer).set(new Uint8Array(contents), 0);
	return arrayBuffer;
};

// With `ArrayBuffer.resize()`, `contents` size matches exactly the size of
// the stream data. It does not include extraneous zeroes to trim at the end.
// The underlying `ArrayBuffer` does allocate a number of bytes that is a power
// of 2, but those bytes are only visible after calling `ArrayBuffer.resize()`.
const resizeArrayBuffer = (contents, length) => {
	if (length <= contents.maxByteLength) {
		contents.resize(length);
		return contents;
	}

	const arrayBuffer = new ArrayBuffer(length, {maxByteLength: getNewContentsLength(length)});
	new Uint8Array(arrayBuffer).set(new Uint8Array(contents), 0);
	return arrayBuffer;
};

// Retrieve the closest `length` that is both >= and a power of 2
const getNewContentsLength = length => SCALE_FACTOR ** Math.ceil(Math.log(length) / Math.log(SCALE_FACTOR));

const SCALE_FACTOR = 2;

const finalizeArrayBuffer = ({contents, length}) => hasArrayBufferResize() ? contents : contents.slice(0, length);

// `ArrayBuffer.slice()` is slow. When `ArrayBuffer.resize()` is available
// (Node >=20.0.0, Safari >=16.4 and Chrome), we can use it instead.
// eslint-disable-next-line no-warning-comments
// TODO: remove after dropping support for Node 20.
// eslint-disable-next-line no-warning-comments
// TODO: use `ArrayBuffer.transferToFixedLength()` instead once it is available
const hasArrayBufferResize = () => 'resize' in ArrayBuffer.prototype;

const arrayBufferMethods = {
	init: initArrayBuffer,
	convertChunk: {
		string: useTextEncoder,
		buffer: useUint8Array,
		arrayBuffer: useUint8Array,
		dataView: useUint8ArrayWithOffset,
		typedArray: useUint8ArrayWithOffset,
		others: throwObjectStream,
	},
	getSize: getLengthProp,
	truncateChunk: truncateArrayBufferChunk,
	addChunk: addArrayBufferChunk,
	getFinalChunk: noop,
	finalize: finalizeArrayBuffer,
};


code:
import {getStreamContents} from './contents.js';
import {identity, noop, getContentsProp} from './utils.js';

export async function getStreamAsArray(stream, options) {
	return getStreamContents(stream, arrayMethods, options);
}

const initArray = () => ({contents: []});

const increment = () => 1;

const addArrayChunk = (convertedChunk, {contents}) => {
	contents.push(convertedChunk);
	return contents;
};

const arrayMethods = {
	init: initArray,
	convertChunk: {
		string: identity,
		buffer: identity,
		arrayBuffer: identity,
		dataView: identity,
		typedArray: identity,
		others: identity,
	},
	getSize: increment,
	truncateChunk: noop,
	addChunk: addArrayChunk,
	getFinalChunk: noop,
	finalize: getContentsProp,
};


code:
import {getStreamAsArrayBuffer} from './array-buffer.js';

export async function getStreamAsBuffer(stream, options) {
	if (!('Buffer' in globalThis)) {
		throw new Error('getStreamAsBuffer() is only supported in Node.js');
	}

	try {
		return arrayBufferToNodeBuffer(await getStreamAsArrayBuffer(stream, options));
	} catch (error) {
		if (error.bufferedData !== undefined) {
			error.bufferedData = arrayBufferToNodeBuffer(error.bufferedData);
		}

		throw error;
	}
}

// eslint-disable-next-line n/prefer-global/buffer
const arrayBufferToNodeBuffer = arrayBuffer => globalThis.Buffer.from(arrayBuffer);


code:
export const getStreamContents = async (stream, {init, convertChunk, getSize, truncateChunk, addChunk, getFinalChunk, finalize}, {maxBuffer = Number.POSITIVE_INFINITY} = {}) => {
	if (!isAsyncIterable(stream)) {
		throw new Error('The first argument must be a Readable, a ReadableStream, or an async iterable.');
	}

	const state = init();
	state.length = 0;

	try {
		for await (const chunk of stream) {
			const chunkType = getChunkType(chunk);
			const convertedChunk = convertChunk[chunkType](chunk, state);
			appendChunk({convertedChunk, state, getSize, truncateChunk, addChunk, maxBuffer});
		}

		appendFinalChunk({state, convertChunk, getSize, truncateChunk, addChunk, getFinalChunk, maxBuffer});
		return finalize(state);
	} catch (error) {
		error.bufferedData = finalize(state);
		throw error;
	}
};

const appendFinalChunk = ({state, getSize, truncateChunk, addChunk, getFinalChunk, maxBuffer}) => {
	const convertedChunk = getFinalChunk(state);
	if (convertedChunk !== undefined) {
		appendChunk({convertedChunk, state, getSize, truncateChunk, addChunk, maxBuffer});
	}
};

const appendChunk = ({convertedChunk, state, getSize, truncateChunk, addChunk, maxBuffer}) => {
	const chunkSize = getSize(convertedChunk);
	const newLength = state.length + chunkSize;

	if (newLength <= maxBuffer) {
		addNewChunk(convertedChunk, state, addChunk, newLength);
		return;
	}

	const truncatedChunk = truncateChunk(convertedChunk, maxBuffer - state.length);

	if (truncatedChunk !== undefined) {
		addNewChunk(truncatedChunk, state, addChunk, maxBuffer);
	}

	throw new MaxBufferError();
};

const addNewChunk = (convertedChunk, state, addChunk, newLength) => {
	state.contents = addChunk(convertedChunk, state, newLength);
	state.length = newLength;
};

const isAsyncIterable = stream => typeof stream === 'object' && stream !== null && typeof stream[Symbol.asyncIterator] === 'function';

const getChunkType = chunk => {
	const typeOfChunk = typeof chunk;

	if (typeOfChunk === 'string') {
		return 'string';
	}

	if (typeOfChunk !== 'object' || chunk === null) {
		return 'others';
	}

	// eslint-disable-next-line n/prefer-global/buffer
	if (globalThis.Buffer?.isBuffer(chunk)) {
		return 'buffer';
	}

	const prototypeName = objectToString.call(chunk);

	if (prototypeName === '[object ArrayBuffer]') {
		return 'arrayBuffer';
	}

	if (prototypeName === '[object DataView]') {
		return 'dataView';
	}

	if (
		Number.isInteger(chunk.byteLength)
		&& Number.isInteger(chunk.byteOffset)
		&& objectToString.call(chunk.buffer) === '[object ArrayBuffer]'
	) {
		return 'typedArray';
	}

	return 'others';
};

const {toString: objectToString} = Object.prototype;

export class MaxBufferError extends Error {
	name = 'MaxBufferError';

	constructor() {
		super('maxBuffer exceeded');
	}
}


code:
import {type Readable} from 'node:stream';
import {type Buffer} from 'node:buffer';

export class MaxBufferError extends Error {
	readonly name: 'MaxBufferError';
	constructor();
}

type TextStreamItem = string | Buffer | ArrayBuffer | ArrayBufferView;
export type AnyStream<SteamItem = TextStreamItem> = Readable | ReadableStream<SteamItem> | AsyncIterable<SteamItem>;

export type Options = {
	/**
	Maximum length of the stream. If exceeded, the promise will be rejected with a `MaxBufferError`.

	Depending on the [method](#api), the length is measured with [`string.length`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length), [`buffer.length`](https://nodejs.org/api/buffer.html#buflength), [`arrayBuffer.byteLength`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/byteLength) or [`array.length`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length).

	@default Infinity
	*/
	readonly maxBuffer?: number;
};

/**
Get the given `stream` as a string.

@returns The stream's contents as a promise.

@example
```
import fs from 'node:fs';
import getStream from 'get-stream';

const stream = fs.createReadStream('unicorn.txt');

console.log(await getStream(stream));
//               ,,))))))));,
//            __)))))))))))))),
// \|/       -\(((((''''((((((((.
// -*-==//////((''  .     `)))))),
// /|\      ))| o    ;-.    '(((((                                  ,(,
//          ( `|    /  )    ;))))'                               ,_))^;(~
//             |   |   |   ,))((((_     _____------~~~-.        %,;(;(>';'~
//             o_);   ;    )))(((` ~---~  `::           \      %%~~)(v;(`('~
//                   ;    ''''````         `:       `:::|\,__,%%    );`'; ~
//                  |   _                )     /      `:|`----'     `-'
//            ______/\/~    |                 /        /
//          /~;;.____/;;'  /          ___--,-(   `;;;/
//         / //  _;______;'------~~~~~    /;;/\    /
//        //  | |                        / ;   \;;,\
//       (<_  | ;                      /',/-----'  _>
//        \_| ||_                     //~;~~~~~~~~~
//            `\_|                   (,~~
//                                    \~\
//                                     ~~
```

@example
```
import getStream from 'get-stream';

const {body: readableStream} = await fetch('https://example.com');
console.log(await getStream(readableStream));
```

@example
```
import {opendir} from 'node:fs/promises';
import {getStreamAsArray} from 'get-stream';

const asyncIterable = await opendir(directory);
console.log(await getStreamAsArray(asyncIterable));
```
*/
export default function getStream(stream: AnyStream, options?: Options): Promise<string>;

/**
Get the given `stream` as a Node.js [`Buffer`](https://nodejs.org/api/buffer.html#class-buffer).

@returns The stream's contents as a promise.

@example
```
import {getStreamAsBuffer} from 'get-stream';

const stream = fs.createReadStream('unicorn.png');
console.log(await getStreamAsBuffer(stream));
```
*/
export function getStreamAsBuffer(stream: AnyStream, options?: Options): Promise<Buffer>;

/**
Get the given `stream` as an [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer).

@returns The stream's contents as a promise.

@example
```
import {getStreamAsArrayBuffer} from 'get-stream';

const {body: readableStream} = await fetch('https://example.com');
console.log(await getStreamAsArrayBuffer(readableStream));
```
*/
export function getStreamAsArrayBuffer(stream: AnyStream, options?: Options): Promise<ArrayBuffer>;

/**
Get the given `stream` as an array. Unlike [other methods](#api), this supports [streams of objects](https://nodejs.org/api/stream.html#object-mode).

@returns The stream's contents as a promise.

@example
```
import {getStreamAsArray} from 'get-stream';

const {body: readableStream} = await fetch('https://example.com');
console.log(await getStreamAsArray(readableStream));
```
*/
export function getStreamAsArray<Item>(stream: AnyStream<Item>, options?: Options): Promise<Item[]>;


code:
export {getStreamAsArray} from './array.js';
export {getStreamAsArrayBuffer} from './array-buffer.js';
export {getStreamAsBuffer} from './buffer.js';
export {getStreamAsString as default} from './string.js';
export {MaxBufferError} from './contents.js';


code:
import {getStreamContents} from './contents.js';
import {identity, getContentsProp, throwObjectStream, getLengthProp} from './utils.js';

export async function getStreamAsString(stream, options) {
	return getStreamContents(stream, stringMethods, options);
}

const initString = () => ({contents: '', textDecoder: new TextDecoder()});

const useTextDecoder = (chunk, {textDecoder}) => textDecoder.decode(chunk, {stream: true});

const addStringChunk = (convertedChunk, {contents}) => contents + convertedChunk;

const truncateStringChunk = (convertedChunk, chunkSize) => convertedChunk.slice(0, chunkSize);

const getFinalStringChunk = ({textDecoder}) => {
	const finalChunk = textDecoder.decode();
	return finalChunk === '' ? undefined : finalChunk;
};

const stringMethods = {
	init: initString,
	convertChunk: {
		string: identity,
		buffer: useTextDecoder,
		arrayBuffer: useTextDecoder,
		dataView: useTextDecoder,
		typedArray: useTextDecoder,
		others: throwObjectStream,
	},
	getSize: getLengthProp,
	truncateChunk: truncateStringChunk,
	addChunk: addStringChunk,
	getFinalChunk: getFinalStringChunk,
	finalize: getContentsProp,
};


code:
export const identity = value => value;

export const noop = () => undefined;

export const getContentsProp = ({contents}) => contents;

export const throwObjectStream = chunk => {
	throw new Error(`Streams in object mode are not supported: ${String(chunk)}`);
};

export const getLengthProp = convertedChunk => convertedChunk.length;


code:
[![Node](https://img.shields.io/badge/-Node.js-808080?logo=node.js&colorA=404040&logoColor=66cc33)](https://www.npmjs.com/package/human-signals)
[![TypeScript](https://img.shields.io/badge/-Typed-808080?logo=typescript&colorA=404040&logoColor=0096ff)](/src/main.d.ts)
[![Codecov](https://img.shields.io/badge/-Tested%20100%25-808080?logo=codecov&colorA=404040)](https://codecov.io/gh/ehmicky/human-signals)
[![Mastodon](https://img.shields.io/badge/-Mastodon-808080.svg?logo=mastodon&colorA=404040&logoColor=9590F9)](https://fosstodon.org/@ehmicky)
[![Medium](https://img.shields.io/badge/-Medium-808080.svg?logo=medium&colorA=404040)](https://medium.com/@ehmicky)

Human-friendly process signals.

This is a map of known process signals with some information about each signal.

Unlike
[`os.constants.signals`](https://nodejs.org/api/os.html#os_signal_constants)
this includes:

- human-friendly [descriptions](#description)
- [default actions](#action), including whether they [can be prevented](#forced)
- whether the signal is [supported](#supported) by the current OS

# Example

```js
import { signalsByName, signalsByNumber } from 'human-signals'

console.log(signalsByName.SIGINT)
// {
//   name: 'SIGINT',
//   number: 2,
//   description: 'User interruption with CTRL-C',
//   supported: true,
//   action: 'terminate',
//   forced: false,
//   standard: 'ansi'
// }

console.log(signalsByNumber[8])
// {
//   name: 'SIGFPE',
//   number: 8,
//   description: 'Floating point arithmetic error',
//   supported: true,
//   action: 'core',
//   forced: false,
//   standard: 'ansi'
// }
```

# Install

```bash
npm install human-signals
```

This package works in Node.js >=16.17.0.

This is an ES module. It must be loaded using
[an `import` or `import()` statement](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c),
not `require()`. If TypeScript is used, it must be configured to
[output ES modules](https://www.typescriptlang.org/docs/handbook/esm-node.html),
not CommonJS.

# Usage

## signalsByName

_Type_: `object`

Object whose keys are signal [names](#name) and values are
[signal objects](#signal).

## signalsByNumber

_Type_: `object`

Object whose keys are signal [numbers](#number) and values are
[signal objects](#signal).

## signal

_Type_: `object`

Signal object with the following properties.

### name

_Type_: `string`

Standard name of the signal, for example `'SIGINT'`.

### number

_Type_: `number`

Code number of the signal, for example `2`. While most `number` are
cross-platform, some are different between different OS.

### description

_Type_: `string`

Human-friendly description for the signal, for example
`'User interruption with CTRL-C'`.

### supported

_Type_: `boolean`

Whether the current OS can handle this signal in Node.js using
[`process.on(name, handler)`](https://nodejs.org/api/process.html#process_signal_events).

The list of supported signals
[is OS-specific](https://github.com/ehmicky/cross-platform-node-guide/blob/main/docs/6_networking_ipc/signals.md#cross-platform-signals).

### action

_Type_: `string`\
_Enum_: `'terminate'`, `'core'`, `'ignore'`, `'pause'`, `'unpause'`

What is the default action for this signal when it is not handled.

### forced

_Type_: `boolean`

Whether the signal's default action cannot be prevented. This is `true` for
`SIGTERM`, `SIGKILL` and `SIGSTOP`.

### standard

_Type_: `string`\
_Enum_: `'ansi'`, `'posix'`, `'bsd'`, `'systemv'`, `'other'`

Which standard defined that signal.

# Support

For any question, _don't hesitate_ to [submit an issue on GitHub](../../issues).

Everyone is welcome regardless of personal background. We enforce a
[Code of conduct](CODE_OF_CONDUCT.md) in order to promote a positive and
inclusive environment.

# Contributing

This project was made with ❤. The simplest way to give back is by starring and
sharing it online.

If the documentation is unclear or has a typo, please click on the page's `Edit`
button (pencil icon) and suggest a correction.

If you would like to help us fix a bug or add a new feature, please check our
[guidelines](CONTRIBUTING.md). Pull requests are welcome!

Thanks go to our wonderful contributors:

ehmicky
💻 🎨 🤔 📖

electrovir
💻


code:
export const SIGNALS=[
{
name:"SIGHUP",
number:1,
action:"terminate",
description:"Terminal closed",
standard:"posix"
},
{
name:"SIGINT",
number:2,
action:"terminate",
description:"User interruption with CTRL-C",
standard:"ansi"
},
{
name:"SIGQUIT",
number:3,
action:"core",
description:"User interruption with CTRL-\\",
standard:"posix"
},
{
name:"SIGILL",
number:4,
action:"core",
description:"Invalid machine instruction",
standard:"ansi"
},
{
name:"SIGTRAP",
number:5,
action:"core",
description:"Debugger breakpoint",
standard:"posix"
},
{
name:"SIGABRT",
number:6,
action:"core",
description:"Aborted",
standard:"ansi"
},
{
name:"SIGIOT",
number:6,
action:"core",
description:"Aborted",
standard:"bsd"
},
{
name:"SIGBUS",
number:7,
action:"core",
description:
"Bus error due to misaligned, non-existing address or paging error",
standard:"bsd"
},
{
name:"SIGEMT",
number:7,
action:"terminate",
description:"Command should be emulated but is not implemented",
standard:"other"
},
{
name:"SIGFPE",
number:8,
action:"core",
description:"Floating point arithmetic error",
standard:"ansi"
},
{
name:"SIGKILL",
number:9,
action:"terminate",
description:"Forced termination",
standard:"posix",
forced:true
},
{
name:"SIGUSR1",
number:10,
action:"terminate",
description:"Application-specific signal",
standard:"posix"
},
{
name:"SIGSEGV",
number:11,
action:"core",
description:"Segmentation fault",
standard:"ansi"
},
{
name:"SIGUSR2",
number:12,
action:"terminate",
description:"Application-specific signal",
standard:"posix"
},
{
name:"SIGPIPE",
number:13,
action:"terminate",
description:"Broken pipe or socket",
standard:"posix"
},
{
name:"SIGALRM",
number:14,
action:"terminate",
description:"Timeout or timer",
standard:"posix"
},
{
name:"SIGTERM",
number:15,
action:"terminate",
description:"Termination",
standard:"ansi"
},
{
name:"SIGSTKFLT",
number:16,
action:"terminate",
description:"Stack is empty or overflowed",
standard:"other"
},
{
name:"SIGCHLD",
number:17,
action:"ignore",
description:"Child process terminated, paused or unpaused",
standard:"posix"
},
{
name:"SIGCLD",
number:17,
action:"ignore",
description:"Child process terminated, paused or unpaused",
standard:"other"
},
{
name:"SIGCONT",
number:18,
action:"unpause",
description:"Unpaused",
standard:"posix",
forced:true
},
{
name:"SIGSTOP",
number:19,
action:"pause",
description:"Paused",
standard:"posix",
forced:true
},
{
name:"SIGTSTP",
number:20,
action:"pause",
description:"Paused using CTRL-Z or \"suspend\"",
standard:"posix"
},
{
name:"SIGTTIN",
number:21,
action:"pause",
description:"Background process cannot read terminal input",
standard:"posix"
},
{
name:"SIGBREAK",
number:21,
action:"terminate",
description:"User interruption with CTRL-BREAK",
standard:"other"
},
{
name:"SIGTTOU",
number:22,
action:"pause",
description:"Background process cannot write to terminal output",
standard:"posix"
},
{
name:"SIGURG",
number:23,
action:"ignore",
description:"Socket received out-of-band data",
standard:"bsd"
},
{
name:"SIGXCPU",
number:24,
action:"core",
description:"Process timed out",
standard:"bsd"
},
{
name:"SIGXFSZ",
number:25,
action:"core",
description:"File too big",
standard:"bsd"
},
{
name:"SIGVTALRM",
number:26,
action:"terminate",
description:"Timeout or timer",
standard:"bsd"
},
{
name:"SIGPROF",
number:27,
action:"terminate",
description:"Timeout or timer",
standard:"bsd"
},
{
name:"SIGWINCH",
number:28,
action:"ignore",
description:"Terminal window size changed",
standard:"bsd"
},
{
name:"SIGIO",
number:29,
action:"terminate",
description:"I/O is available",
standard:"other"
},
{
name:"SIGPOLL",
number:29,
action:"terminate",
description:"Watched event",
standard:"other"
},
{
name:"SIGINFO",
number:29,
action:"ignore",
description:"Request for process information",
standard:"other"
},
{
name:"SIGPWR",
number:30,
action:"terminate",
description:"Device running out of power",
standard:"systemv"
},
{
name:"SIGSYS",
number:31,
action:"core",
description:"Invalid system call",
standard:"other"
},
{
name:"SIGUNUSED",
number:31,
action:"terminate",
description:"Invalid system call",
standard:"other"
}];


code:
/**
 * What is the default action for this signal when it is not handled.
 */
export type SignalAction = 'terminate' | 'core' | 'ignore' | 'pause' | 'unpause'

/**
 * Which standard defined that signal.
 */
export type SignalStandard = 'ansi' | 'posix' | 'bsd' | 'systemv' | 'other'

/**
 * Standard name of the signal, for example 'SIGINT'.
 */
export type SignalName = `SIG${string}`

/**
 * Code number of the signal, for example 2.
 * While most number are cross-platform, some are different between different
 * OS.
 */
export type SignalNumber = number

export interface Signal {
  /**
   * Standard name of the signal, for example 'SIGINT'.
   */
  name: SignalName

  /**
   * Code number of the signal, for example 2.
   * While most number are cross-platform, some are different between different
   * OS.
   */
  number: SignalNumber

  /**
   * Human-friendly description for the signal, for example
   * 'User interruption with CTRL-C'.
   */
  description: string

  /**
   * Whether the current OS can handle this signal in Node.js using
   * `process.on(name, handler)`. The list of supported signals is OS-specific.
   */
  supported: boolean

  /**
   * What is the default action for this signal when it is not handled.
   */
  action: SignalAction

  /**
   * Whether the signal's default action cannot be prevented.
   * This is true for SIGTERM, SIGKILL and SIGSTOP.
   */
  forced: boolean

  /**
   * Which standard defined that signal.
   */
  standard: SignalStandard
}

/**
 * Object whose keys are signal names and values are signal objects.
 */
export declare const signalsByName: { [signalName: SignalName]: Signal }

/**
 * Object whose keys are signal numbers and values are signal objects.
 */
export declare const signalsByNumber: { [signalNumber: SignalNumber]: Signal }


code:
import{constants}from"node:os";

import{SIGRTMAX}from"./realtime.js";
import{getSignals}from"./signals.js";

const getSignalsByName=()=>{
const signals=getSignals();
return Object.fromEntries(signals.map(getSignalByName))
};

const getSignalByName=({
name,
number,
description,
supported,
action,
forced,
standard
})=>[name,{name,number,description,supported,action,forced,standard}];

export const signalsByName=getSignalsByName();

const getSignalsByNumber=()=>{
const signals=getSignals();
const length=SIGRTMAX+1;
const signalsA=Array.from({length},(value,number)=>
getSignalByNumber(number,signals)
);
return Object.assign({},...signalsA)
};

const getSignalByNumber=(number,signals)=>{
const signal=findSignalByNumber(number,signals);

if(signal===undefined){
return{}
}

const{name,description,supported,action,forced,standard}=signal;
return{
[number]:{
name,
number,
description,
supported,
action,
forced,
standard
}
}
};

const findSignalByNumber=(number,signals)=>{
const signal=signals.find(({name})=>constants.signals[name]===number);

if(signal!==undefined){
return signal
}

return signals.find((signalA)=>signalA.number===number)
};

export const signalsByNumber=getSignalsByNumber();


code:
export const getRealtimeSignals=()=>{
const length=SIGRTMAX-SIGRTMIN+1;
return Array.from({length},getRealtimeSignal)
};

const getRealtimeSignal=(value,index)=>({
name:`SIGRT${index+1}`,
number:SIGRTMIN+index,
action:"terminate",
description:"Application-specific signal (realtime)",
standard:"posix"
});

const SIGRTMIN=34;
export const SIGRTMAX=64;


code:
import{constants}from"node:os";

import{SIGNALS}from"./core.js";
import{getRealtimeSignals}from"./realtime.js";

export const getSignals=()=>{
const realtimeSignals=getRealtimeSignals();
const signals=[...SIGNALS,...realtimeSignals].map(normalizeSignal);
return signals
};

const normalizeSignal=({
name,
number:defaultNumber,
description,
action,
forced=false,
standard
})=>{
const{
signals:{[name]:constantSignal}
}=constants;
const supported=constantSignal!==undefined;
const number=supported?constantSignal:defaultNumber;
return{name,number,description,supported,action,forced,standard}
};


human-signals

test: gulp test

description: Human-friendly process signals

keywords: signal signals handlers error-handling interrupts sigterm sigint irq process exit exit-code status operating-system es6 javascript typescript linux macos windows nodejs

repository: ehmicky/human-signals

lib: src


code:
import {
	Stream,
	Writable as WritableStream,
	Readable as ReadableStream,
	Duplex as DuplexStream,
	Transform as TransformStream,
} from 'node:stream';

/**
@returns Whether `stream` is a [`Stream`](https://nodejs.org/api/stream.html#stream_stream).

@example
```
import fs from 'node:fs';
import {isStream} from 'is-stream';

isStream(fs.createReadStream('unicorn.png'));
//=> true

isStream({});
//=> false
```
*/
export function isStream(stream: unknown): stream is Stream;

/**
@returns Whether `stream` is a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable).

@example
```
import fs from 'node:fs';
import {isWritableStream} from 'is-stream';

isWritableStream(fs.createWriteStrem('unicorn.txt'));
//=> true
```
*/
export function isWritableStream(stream: unknown): stream is WritableStream;

/**
@returns Whether `stream` is a [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream_readable).

@example
```
import fs from 'node:fs';
import {isReadableStream} from 'is-stream';

isReadableStream(fs.createReadStream('unicorn.png'));
//=> true
```
*/
export function isReadableStream(stream: unknown): stream is ReadableStream;

/**
@returns Whether `stream` is a [`stream.Duplex`](https://nodejs.org/api/stream.html#stream_class_stream_duplex).

@example
```
import {Duplex as DuplexStream} from 'node:stream';
import {isDuplexStream} from 'is-stream';

isDuplexStream(new DuplexStream());
//=> true
```
*/
export function isDuplexStream(stream: unknown): stream is DuplexStream;

/**
@returns Whether `stream` is a [`stream.Transform`](https://nodejs.org/api/stream.html#stream_class_stream_transform).

@example
```
import fs from 'node:fs';
import StringifyStream from 'streaming-json-stringify';
import {isTransformStream} from 'is-stream';

isTransformStream(StringifyStream());
//=> true
```
*/
export function isTransformStream(stream: unknown): stream is TransformStream;


code:
export function isStream(stream) {
	return stream !== null
		&& typeof stream === 'object'
		&& typeof stream.pipe === 'function';
}

export function isWritableStream(stream) {
	return isStream(stream)
		&& stream.writable !== false
		&& typeof stream._write === 'function'
		&& typeof stream._writableState === 'object';
}

export function isReadableStream(stream) {
	return isStream(stream)
		&& stream.readable !== false
		&& typeof stream._read === 'function'
		&& typeof stream._readableState === 'object';
}

export function isDuplexStream(stream) {
	return isWritableStream(stream)
		&& isReadableStream(stream);
}

export function isTransformStream(stream) {
	return isDuplexStream(stream)
		&& typeof stream._transform === 'function';
}


is-stream

description: Check if something is a Node.js stream

repository: sindresorhus/is-stream

test: xo && ava && tsd

keywords: stream type streams writable readable duplex transform check detect is


code:
# is-stream

> Check if something is a [Node.js stream](https://nodejs.org/api/stream.html)

## Install

```
$ npm install is-stream
```

## Usage

```js
import fs from 'node:fs';
import {isStream} from 'is-stream';

isStream(fs.createReadStream('unicorn.png'));
//=> true

isStream({});
//=> false
```

## API

### isStream(stream)

Returns a `boolean` for whether it's a [`Stream`](https://nodejs.org/api/stream.html#stream_stream).

#### isWritableStream(stream)

Returns a `boolean` for whether it's a [`stream.Writable`](https://nodejs.org/api/stream.html#stream_class_stream_writable).

#### isReadableStream(stream)

Returns a `boolean` for whether it's a [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream_readable).

#### isDuplexStream(stream)

Returns a `boolean` for whether it's a [`stream.Duplex`](https://nodejs.org/api/stream.html#stream_class_stream_duplex).

#### isTransformStream(stream)

Returns a `boolean` for whether it's a [`stream.Transform`](https://nodejs.org/api/stream.html#stream_class_stream_transform).

## Related

- [is-file-stream](https://github.com/jamestalmage/is-file-stream) - Detect if a stream is a file stream

---

		Get professional support for this package with a Tidelift subscription

		Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies.


lilconfig

description: A zero-dependency alternative to cosmiconfig

lint: biome ci ./src

types: tsc

keywords: cosmiconfig config configuration search

type: git


code:
# Lilconfig ⚙
[![npm version](https://badge.fury.io/js/lilconfig.svg)](https://badge.fury.io/js/lilconfig)
[![install size](https://packagephobia.now.sh/badge?p=lilconfig)](https://packagephobia.now.sh/result?p=lilconfig)
[![Coverage Status](https://coveralls.io/repos/github/antonk52/lilconfig/badge.svg)](https://coveralls.io/github/antonk52/lilconfig)

A zero-dependency alternative to [cosmiconfig](https://www.npmjs.com/package/cosmiconfig) with the same API.

## Installation

```sh
npm install lilconfig
```

## Usage

```js
import {lilconfig, lilconfigSync} from 'lilconfig';

// all keys are optional
const options = {
    stopDir: '/Users/you/some/dir',
    searchPlaces: ['package.json', 'myapp.conf.js'],
    ignoreEmptySearchPlaces: false
}

lilconfig(
    'myapp',
    options // optional
).search() // Promise<LilconfigResult>

lilconfigSync(
    'myapp',
    options // optional
).load(pathToConfig) // LilconfigResult

/**
 * LilconfigResult
 * {
 *   config: any; // your config
 *   filepath: string;
 * }
 */
```

## ESM

ESM configs can be loaded with **async API only**. Specifically `js` files in projects with `"type": "module"` in `package.json` or `mjs` files.

## Difference to `cosmiconfig`
Lilconfig does not intend to be 100% compatible with `cosmiconfig` but tries to mimic it where possible. The key difference is **no** support for yaml files out of the box(`lilconfig` attempts to parse files with no extension as JSON instead of YAML). You can still add the support for YAML files by providing a loader, see an [example](#yaml-loader) below.

### Options difference between the two.

|cosmiconfig option      | lilconfig |
|------------------------|-----------|
|cache                   | ✅        |
|loaders                 | ✅        |
|ignoreEmptySearchPlaces | ✅        |
|packageProp             | ✅        |
|searchPlaces            | ✅        |
|stopDir                 | ✅        |
|transform               | ✅        |

## Loaders examples

### Yaml loader

If you need the YAML support you can provide your own loader

```js
import {lilconfig} from 'lilconfig';
import yaml from 'yaml';

function loadYaml(filepath, content) {
    return yaml.parse(content);
}

const options = {
    loaders: {
        '.yaml': loadYaml,
        '.yml': loadYaml,
        // loader for files with no extension
        noExt: loadYaml
    }
};

lilconfig('myapp', options)
    .search()
    .then(result => {
        result // {config, filepath}
    });
```

## Version correlation

- lilconig v1 → cosmiconfig v6
- lilconig v2 → cosmiconfig v7
- lilconig v3 → cosmiconfig v8


code:
export type LilconfigResult = null | {
	filepath: string;
	config: any;
	isEmpty?: boolean;
};
interface OptionsBase {
	cache?: boolean;
	stopDir?: string;
	searchPlaces?: string[];
	ignoreEmptySearchPlaces?: boolean;
	packageProp?: string | string[];
}
export type Transform =
	| TransformSync
	| ((result: LilconfigResult) => Promise<LilconfigResult>);
export type TransformSync = (result: LilconfigResult) => LilconfigResult;
type LoaderResult = any;
export type LoaderSync = (filepath: string, content: string) => LoaderResult;
export type Loader =
	| LoaderSync
	| ((filepath: string, content: string) => Promise<LoaderResult>);
export type Loaders = Record<string, Loader>;
export type LoadersSync = Record<string, LoaderSync>;
export interface Options extends OptionsBase {
	loaders?: Loaders;
	transform?: Transform;
}
export interface OptionsSync extends OptionsBase {
	loaders?: LoadersSync;
	transform?: TransformSync;
}
export declare const defaultLoadersSync: LoadersSync;
export declare const defaultLoaders: Loaders;
type ClearCaches = {
	clearLoadCache: () => void;
	clearSearchCache: () => void;
	clearCaches: () => void;
};
type AsyncSearcher = {
	search(searchFrom?: string): Promise<LilconfigResult>;
	load(filepath: string): Promise<LilconfigResult>;
} & ClearCaches;
export declare function lilconfig(
	name: string,
	options?: Partial<Options>,
): AsyncSearcher;
type SyncSearcher = {
	search(searchFrom?: string): LilconfigResult;
	load(filepath: string): LilconfigResult;
} & ClearCaches;
export declare function lilconfigSync(
	name: string,
	options?: OptionsSync,
): SyncSearcher;


code:
// @ts-check
const path = require('path');
const fs = require('fs');
const os = require('os');

const fsReadFileAsync = fs.promises.readFile;

/** @type {(name: string, sync: boolean) => string[]} */
function getDefaultSearchPlaces(name, sync) {
	return [
		'package.json',
		`.${name}rc.json`,
		`.${name}rc.js`,
		`.${name}rc.cjs`,
		...(sync ? [] : [`.${name}rc.mjs`]),
		`.config/${name}rc`,
		`.config/${name}rc.json`,
		`.config/${name}rc.js`,
		`.config/${name}rc.cjs`,
		...(sync ? [] : [`.config/${name}rc.mjs`]),
		`${name}.config.js`,
		`${name}.config.cjs`,
		...(sync ? [] : [`${name}.config.mjs`]),
	];
}

/**
 * @type {(p: string) => string}
 *
 * see #17
 * On *nix, if cwd is not under homedir,
 * the last path will be '', ('/build' -> '')
 * but it should be '/' actually.
 * And on Windows, this will never happen. ('C:\build' -> 'C:')
 */
function parentDir(p) {
	return path.dirname(p) || path.sep;
}

/** @type {import('./index').LoaderSync} */
const jsonLoader = (_, content) => JSON.parse(content);
// Use plain require in webpack context for dynamic import
const requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require;
/** @type {import('./index').LoadersSync} */
const defaultLoadersSync = Object.freeze({
	'.js': requireFunc,
	'.json': requireFunc,
	'.cjs': requireFunc,
	noExt: jsonLoader,
});
module.exports.defaultLoadersSync = defaultLoadersSync;

/** @type {import('./index').Loader} */
const dynamicImport = async id => {
	try {
		const mod = await import(/* webpackIgnore: true */ id);

		return mod.default;
	} catch (e) {
		try {
			return requireFunc(id);
		} catch (/** @type {any} */ requireE) {
			if (
				requireE.code === 'ERR_REQUIRE_ESM' ||
				(requireE instanceof SyntaxError &&
					requireE
						.toString()
						.includes('Cannot use import statement outside a module'))
			) {
				throw e;
			}
			throw requireE;
		}
	}
};

/** @type {import('./index').Loaders} */
const defaultLoaders = Object.freeze({
	'.js': dynamicImport,
	'.mjs': dynamicImport,
	'.cjs': dynamicImport,
	'.json': jsonLoader,
	noExt: jsonLoader,
});
module.exports.defaultLoaders = defaultLoaders;

/**
 * @param {string} name
 * @param {import('./index').Options | import('./index').OptionsSync} options
 * @param {boolean} sync
 * @returns {Required<import('./index').Options | import('./index').OptionsSync>}
 */
function getOptions(name, options, sync) {
	/** @type {Required<import('./index').Options>} */
	const conf = {
		stopDir: os.homedir(),
		searchPlaces: getDefaultSearchPlaces(name, sync),
		ignoreEmptySearchPlaces: true,
		cache: true,
		transform: x => x,
		packageProp: [name],
		...options,
		loaders: {
			...(sync ? defaultLoadersSync : defaultLoaders),
			...options.loaders,
		},
	};
	conf.searchPlaces.forEach(place => {
		const key = path.extname(place) || 'noExt';
		const loader = conf.loaders[key];
		if (!loader) {
			throw new Error(`Missing loader for extension "${place}"`);
		}

		if (typeof loader !== 'function') {
			throw new Error(
				`Loader for extension "${place}" is not a function: Received ${typeof loader}.`,
			);
		}
	});

	return conf;
}

/** @type {(props: string | string[], obj: Record<string, any>) => unknown} */
function getPackageProp(props, obj) {
	if (typeof props === 'string' && props in obj) return obj[props];
	return (
		(Array.isArray(props) ? props : props.split('.')).reduce(
			(acc, prop) => (acc === undefined ? acc : acc[prop]),
			obj,
		) || null
	);
}

/** @param {string} filepath */
function validateFilePath(filepath) {
	if (!filepath) throw new Error('load must pass a non-empty string');
}

/** @type {(loader: import('./index').Loader, ext: string) => void} */
function validateLoader(loader, ext) {
	if (!loader) throw new Error(`No loader specified for extension "${ext}"`);
	if (typeof loader !== 'function') throw new Error('loader is not a function');
}

/** @type {(enableCache: boolean) => <T>(c: Map<string, T>, filepath: string, res: T) => T} */
const makeEmplace = enableCache => (c, filepath, res) => {
	if (enableCache) c.set(filepath, res);
	return res;
};

/** @type {import('./index').lilconfig} */
module.exports.lilconfig = function lilconfig(name, options) {
	const {
		ignoreEmptySearchPlaces,
		loaders,
		packageProp,
		searchPlaces,
		stopDir,
		transform,
		cache,
	} = getOptions(name, options ?? {}, false);
	const searchCache = new Map();
	const loadCache = new Map();
	const emplace = makeEmplace(cache);

	return {
		async search(searchFrom = process.cwd()) {
			/** @type {import('./index').LilconfigResult} */
			const result = {
				config: null,
				filepath: '',
			};

			/** @type {Set<string>} */
			const visited = new Set();
			let dir = searchFrom;
			dirLoop: while (true) {
				if (cache) {
					const r = searchCache.get(dir);
					if (r !== undefined) {
						for (const p of visited) searchCache.set(p, r);
						return r;
					}
					visited.add(dir);
				}

				for (const searchPlace of searchPlaces) {
					const filepath = path.join(dir, searchPlace);
					try {
						await fs.promises.access(filepath);
					} catch {
						continue;
					}
					const content = String(await fsReadFileAsync(filepath));
					const loaderKey = path.extname(searchPlace) || 'noExt';
					const loader = loaders[loaderKey];

					// handle package.json
					if (searchPlace === 'package.json') {
						const pkg = await loader(filepath, content);
						const maybeConfig = getPackageProp(packageProp, pkg);
						if (maybeConfig != null) {
							result.config = maybeConfig;
							result.filepath = filepath;
							break dirLoop;
						}

						continue;
					}

					// handle other type of configs
					const isEmpty = content.trim() === '';
					if (isEmpty && ignoreEmptySearchPlaces) continue;

					if (isEmpty) {
						result.isEmpty = true;
						result.config = undefined;
					} else {
						validateLoader(loader, loaderKey);
						result.config = await loader(filepath, content);
					}
					result.filepath = filepath;
					break dirLoop;
				}
				if (dir === stopDir || dir === parentDir(dir)) break dirLoop;
				dir = parentDir(dir);
			}

			const transformed =
				// not found
				result.filepath === '' && result.config === null
					? transform(null)
					: transform(result);

			if (cache) {
				for (const p of visited) searchCache.set(p, transformed);
			}

			return transformed;
		},
		async load(filepath) {
			validateFilePath(filepath);
			const absPath = path.resolve(process.cwd(), filepath);
			if (cache && loadCache.has(absPath)) {
				return loadCache.get(absPath);
			}
			const {base, ext} = path.parse(absPath);
			const loaderKey = ext || 'noExt';
			const loader = loaders[loaderKey];
			validateLoader(loader, loaderKey);
			const content = String(await fsReadFileAsync(absPath));

			if (base === 'package.json') {
				const pkg = await loader(absPath, content);
				return emplace(
					loadCache,
					absPath,
					transform({
						config: getPackageProp(packageProp, pkg),
						filepath: absPath,
					}),
				);
			}
			/** @type {import('./index').LilconfigResult} */
			const result = {
				config: null,
				filepath: absPath,
			};
			// handle other type of configs
			const isEmpty = content.trim() === '';
			if (isEmpty && ignoreEmptySearchPlaces)
				return emplace(
					loadCache,
					absPath,
					transform({
						config: undefined,
						filepath: absPath,
						isEmpty: true,
					}),
				);

			// cosmiconfig returns undefined for empty files
			result.config = isEmpty ? undefined : await loader(absPath, content);

			return emplace(
				loadCache,
				absPath,
				transform(isEmpty ? {...result, isEmpty, config: undefined} : result),
			);
		},
		clearLoadCache() {
			if (cache) loadCache.clear();
		},
		clearSearchCache() {
			if (cache) searchCache.clear();
		},
		clearCaches() {
			if (cache) {
				loadCache.clear();
				searchCache.clear();
			}
		},
	};
};

/** @type {import('./index').lilconfigSync} */
module.exports.lilconfigSync = function lilconfigSync(name, options) {
	const {
		ignoreEmptySearchPlaces,
		loaders,
		packageProp,
		searchPlaces,
		stopDir,
		transform,
		cache,
	} = getOptions(name, options ?? {}, true);
	const searchCache = new Map();
	const loadCache = new Map();
	const emplace = makeEmplace(cache);

	return {
		search(searchFrom = process.cwd()) {
			/** @type {import('./index').LilconfigResult} */
			const result = {
				config: null,
				filepath: '',
			};

			/** @type {Set<string>} */
			const visited = new Set();
			let dir = searchFrom;
			dirLoop: while (true) {
				if (cache) {
					const r = searchCache.get(dir);
					if (r !== undefined) {
						for (const p of visited) searchCache.set(p, r);
						return r;
					}
					visited.add(dir);
				}

				for (const searchPlace of searchPlaces) {
					const filepath = path.join(dir, searchPlace);
					try {
						fs.accessSync(filepath);
					} catch {
						continue;
					}
					const loaderKey = path.extname(searchPlace) || 'noExt';
					const loader = loaders[loaderKey];
					const content = String(fs.readFileSync(filepath));

					// handle package.json
					if (searchPlace === 'package.json') {
						const pkg = loader(filepath, content);
						const maybeConfig = getPackageProp(packageProp, pkg);
						if (maybeConfig != null) {
							result.config = maybeConfig;
							result.filepath = filepath;
							break dirLoop;
						}

						continue;
					}

					// handle other type of configs
					const isEmpty = content.trim() === '';
					if (isEmpty && ignoreEmptySearchPlaces) continue;

					if (isEmpty) {
						result.isEmpty = true;
						result.config = undefined;
					} else {
						validateLoader(loader, loaderKey);
						result.config = loader(filepath, content);
					}
					result.filepath = filepath;
					break dirLoop;
				}
				if (dir === stopDir || dir === parentDir(dir)) break dirLoop;
				dir = parentDir(dir);
			}

			const transformed =
				// not found
				result.filepath === '' && result.config === null
					? transform(null)
					: transform(result);

			if (cache) {
				for (const p of visited) searchCache.set(p, transformed);
			}

			return transformed;
		},
		load(filepath) {
			validateFilePath(filepath);
			const absPath = path.resolve(process.cwd(), filepath);
			if (cache && loadCache.has(absPath)) {
				return loadCache.get(absPath);
			}
			const {base, ext} = path.parse(absPath);
			const loaderKey = ext || 'noExt';
			const loader = loaders[loaderKey];
			validateLoader(loader, loaderKey);

			const content = String(fs.readFileSync(absPath));

			if (base === 'package.json') {
				const pkg = loader(absPath, content);
				return transform({
					config: getPackageProp(packageProp, pkg),
					filepath: absPath,
				});
			}
			const result = {
				config: null,
				filepath: absPath,
			};
			// handle other type of configs
			const isEmpty = content.trim() === '';
			if (isEmpty && ignoreEmptySearchPlaces)
				return emplace(
					loadCache,
					absPath,
					transform({
						filepath: absPath,
						config: undefined,
						isEmpty: true,
					}),
				);

			// cosmiconfig returns undefined for empty files
			result.config = isEmpty ? undefined : loader(absPath, content);

			return emplace(
				loadCache,
				absPath,
				transform(isEmpty ? {...result, isEmpty, config: undefined} : result),
			);
		},
		clearLoadCache() {
			if (cache) loadCache.clear();
		},
		clearSearchCache() {
			if (cache) searchCache.clear();
		},
		clearCaches() {
			if (cache) {
				loadCache.clear();
				searchCache.clear();
			}
		},
	};
};


code:
export interface Options {
	/**
	Skip modifying [non-configurable properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor#Description) instead of throwing an error.

	@default false
	*/
	readonly ignoreNonConfigurable?: boolean;
}

/**
Modifies the `to` function to mimic the `from` function. Returns the `to` function.

`name`, `displayName`, and any other properties of `from` are copied. The `length` property is not copied. Prototype, class, and inherited properties are copied.

`to.toString()` will return the same as `from.toString()` but prepended with a `Wrapped with to()` comment.

@param to - Mimicking function.
@param from - Function to mimic.
@returns The modified `to` function.

@example
```
import mimicFunction from 'mimic-fn';

function foo() {}
foo.unicorn = '🦄';

function wrapper() {
	return foo();
}

console.log(wrapper.name);
//=> 'wrapper'

mimicFunction(wrapper, foo);

console.log(wrapper.name);
//=> 'foo'

console.log(wrapper.unicorn);
//=> '🦄'
```
*/
export default function mimicFunction<
	ArgumentsType extends unknown[],
	ReturnType,
	FunctionType extends (...arguments: ArgumentsType) => ReturnType
>(
	to: (...arguments: ArgumentsType) => ReturnType,
	from: FunctionType,
	options?: Options,
): FunctionType;


code:
const copyProperty = (to, from, property, ignoreNonConfigurable) => {
	// `Function#length` should reflect the parameters of `to` not `from` since we keep its body.
	// `Function#prototype` is non-writable and non-configurable so can never be modified.
	if (property === 'length' || property === 'prototype') {
		return;
	}

	// `Function#arguments` and `Function#caller` should not be copied. They were reported to be present in `Reflect.ownKeys` for some devices in React Native (#41), so we explicitly ignore them here.
	if (property === 'arguments' || property === 'caller') {
		return;
	}

	const toDescriptor = Object.getOwnPropertyDescriptor(to, property);
	const fromDescriptor = Object.getOwnPropertyDescriptor(from, property);

	if (!canCopyProperty(toDescriptor, fromDescriptor) && ignoreNonConfigurable) {
		return;
	}

	Object.defineProperty(to, property, fromDescriptor);
};

// `Object.defineProperty()` throws if the property exists, is not configurable and either:
// - one its descriptors is changed
// - it is non-writable and its value is changed
const canCopyProperty = function (toDescriptor, fromDescriptor) {
	return toDescriptor === undefined || toDescriptor.configurable || (
		toDescriptor.writable === fromDescriptor.writable &&
		toDescriptor.enumerable === fromDescriptor.enumerable &&
		toDescriptor.configurable === fromDescriptor.configurable &&
		(toDescriptor.writable || toDescriptor.value === fromDescriptor.value)
	);
};

const changePrototype = (to, from) => {
	const fromPrototype = Object.getPrototypeOf(from);
	if (fromPrototype === Object.getPrototypeOf(to)) {
		return;
	}

	Object.setPrototypeOf(to, fromPrototype);
};

const wrappedToString = (withName, fromBody) => `/* Wrapped ${withName}*/\n${fromBody}`;

const toStringDescriptor = Object.getOwnPropertyDescriptor(Function.prototype, 'toString');
const toStringName = Object.getOwnPropertyDescriptor(Function.prototype.toString, 'name');

// We call `from.toString()` early (not lazily) to ensure `from` can be garbage collected.
// We use `bind()` instead of a closure for the same reason.
// Calling `from.toString()` early also allows caching it in case `to.toString()` is called several times.
const changeToString = (to, from, name) => {
	const withName = name === '' ? '' : `with ${name.trim()}() `;
	const newToString = wrappedToString.bind(null, withName, from.toString());
	// Ensure `to.toString.toString` is non-enumerable and has the same `same`
	Object.defineProperty(newToString, 'name', toStringName);
	Object.defineProperty(to, 'toString', {...toStringDescriptor, value: newToString});
};

export default function mimicFunction(to, from, {ignoreNonConfigurable = false} = {}) {
	const {name} = to;

	for (const property of Reflect.ownKeys(from)) {
		copyProperty(to, from, property, ignoreNonConfigurable);
	}

	changePrototype(to, from);
	changeToString(to, from, name);

	return to;
}


mimic-fn

description: Make a function mimic another one

repository: sindresorhus/mimic-fn

keywords: function mimic imitate rename copy inherit properties name func fn set infer change


code:
> Make a function mimic another one

Useful when you wrap a function in another function and like to preserve the original name and other properties.

## Install

```
$ npm install mimic-fn
```

## Usage

```js
import mimicFunction from 'mimic-fn';

function foo() {}
foo.unicorn = '🦄';

function wrapper() {
	return foo();
}

console.log(wrapper.name);
//=> 'wrapper'

mimicFunction(wrapper, foo);

console.log(wrapper.name);
//=> 'foo'

console.log(wrapper.unicorn);
//=> '🦄'

console.log(String(wrapper));
//=> '/* Wrapped with wrapper() */\nfunction foo() {}'
```

## API

### mimicFunction(to, from, options?)

Modifies the `to` function to mimic the `from` function. Returns the `to` function.

`name`, `displayName`, and any other properties of `from` are copied. The `length` property is not copied. Prototype, class, and inherited properties are copied.

`to.toString()` will return the same as `from.toString()` but prepended with a `Wrapped with to()` comment.

#### to

Type: `Function`

Mimicking function.

#### from

Type: `Function`

Function to mimic.

#### options

Type: `object`

##### ignoreNonConfigurable

Type: `boolean`\
Default: `false`

Skip modifying [non-configurable properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor#Description) instead of throwing an error.

## Related

- [rename-fn](https://github.com/sindresorhus/rename-fn) - Rename a function
- [keep-func-props](https://github.com/ehmicky/keep-func-props) - Wrap a function without changing its name and other properties

---

		Get professional support for this package with a Tidelift subscription

		Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies.


code:
type CommonOptions = {
	/**
	Working directory.

	@default process.cwd()
	*/
	readonly cwd?: string | URL;

	/**
	The path to the current Node.js executable.

	This can be either an absolute path or a path relative to the `cwd` option.

	@default [process.execPath](https://nodejs.org/api/process.html#processexecpath)
	*/
	readonly execPath?: string | URL;

	/**
	Whether to push the current Node.js executable's directory (`execPath` option) to the front of PATH.

	@default true
	*/
	readonly addExecPath?: boolean;

	/**
	Whether to push the locally installed binaries' directory to the front of PATH.

	@default true
	*/
	readonly preferLocal?: boolean;
};

export type RunPathOptions = CommonOptions & {
	/**
	PATH to be appended.

	Set it to an empty string to exclude the default PATH.

	@default [`PATH`](https://github.com/sindresorhus/path-key)
	*/
	readonly path?: string;
};

export type ProcessEnv = Record<string, string | undefined>;

export type EnvOptions = CommonOptions & {
	/**
	Accepts an object of environment variables, like `process.env`, and modifies the PATH using the correct [PATH key](https://github.com/sindresorhus/path-key). Use this if you're modifying the PATH for use in the `child_process` options.

	@default [process.env](https://nodejs.org/api/process.html#processenv)
	*/
	readonly env?: ProcessEnv;
};

/**
Get your [PATH](https://en.wikipedia.org/wiki/PATH_(variable)) prepended with locally installed binaries.

@returns The augmented path string.

@example
```
import childProcess from 'node:child_process';
import {npmRunPath} from 'npm-run-path';

console.log(process.env.PATH);
//=> '/usr/local/bin'

console.log(npmRunPath());
//=> '/Users/sindresorhus/dev/foo/node_modules/.bin:/Users/sindresorhus/dev/node_modules/.bin:/Users/sindresorhus/node_modules/.bin:/Users/node_modules/.bin:/node_modules/.bin:/usr/local/bin'
```
*/
export function npmRunPath(options?: RunPathOptions): string;

/**
Get your [PATH](https://en.wikipedia.org/wiki/PATH_(variable)) prepended with locally installed binaries.

@returns The augmented [`process.env`](https://nodejs.org/api/process.html#process_process_env) object.

@example
```
import childProcess from 'node:child_process';
import {npmRunPathEnv} from 'npm-run-path';

// `foo` is a locally installed binary
childProcess.execFileSync('foo', {
	env: npmRunPathEnv()
});
```
*/
export function npmRunPathEnv(options?: EnvOptions): ProcessEnv;


code:
import process from 'node:process';
import path from 'node:path';
import {fileURLToPath} from 'node:url';
import pathKey from 'path-key';

export const npmRunPath = ({
	cwd = process.cwd(),
	path: pathOption = process.env[pathKey()],
	preferLocal = true,
	execPath = process.execPath,
	addExecPath = true,
} = {}) => {
	const cwdString = cwd instanceof URL ? fileURLToPath(cwd) : cwd;
	const cwdPath = path.resolve(cwdString);
	const result = [];

	if (preferLocal) {
		applyPreferLocal(result, cwdPath);
	}

	if (addExecPath) {
		applyExecPath(result, execPath, cwdPath);
	}

	return [...result, pathOption].join(path.delimiter);
};

const applyPreferLocal = (result, cwdPath) => {
	let previous;

	while (previous !== cwdPath) {
		result.push(path.join(cwdPath, 'node_modules/.bin'));
		previous = cwdPath;
		cwdPath = path.resolve(cwdPath, '..');
	}
};

// Ensure the running `node` binary is used
const applyExecPath = (result, execPath, cwdPath) => {
	const execPathString = execPath instanceof URL ? fileURLToPath(execPath) : execPath;
	result.push(path.resolve(cwdPath, execPathString, '..'));
};

export const npmRunPathEnv = ({env = process.env, ...options} = {}) => {
	env = {...env};

	const pathName = pathKey({env});
	options.path = env[pathName];
	env[pathName] = npmRunPath(options);

	return env;
};


npm-run-path

description: Get your PATH prepended with locally installed binaries

repository: sindresorhus/npm-run-path

keywords: npm run path package bin binary binaries script cli command-line execute executable


code:
# npm-run-path

> Get your [PATH](https://en.wikipedia.org/wiki/PATH_(variable)) prepended with locally installed binaries

In [npm run scripts](https://docs.npmjs.com/cli/run-script) you can execute locally installed binaries by name. This enables the same outside npm.

## Install

```sh
npm install npm-run-path
```

## Usage

```js
import childProcess from 'node:child_process';
import {npmRunPath, npmRunPathEnv} from 'npm-run-path';

console.log(process.env.PATH);
//=> '/usr/local/bin'

console.log(npmRunPath());
//=> '/Users/sindresorhus/dev/foo/node_modules/.bin:/Users/sindresorhus/dev/node_modules/.bin:/Users/sindresorhus/node_modules/.bin:/Users/node_modules/.bin:/node_modules/.bin:/usr/local/bin'

// `foo` is a locally installed binary
childProcess.execFileSync('foo', {
	env: npmRunPathEnv()
});
```

## API

### npmRunPath(options?)

`options`: [`Options`](#options)\
_Returns_: `string`

Returns the augmented PATH string.

### npmRunPathEnv(options?)

`options`: [`Options`](#options)\
_Returns_: `object`

Returns the augmented [`process.env`](https://nodejs.org/api/process.html#process_process_env) object.

### options

Type: `object`

#### cwd

Type: `string | URL`\
Default: `process.cwd()`

The working directory.

#### execPath

Type: `string | URL`\
Default: [`process.execPath`](https://nodejs.org/api/process.html#processexecpath)

The path to the current Node.js executable.

This can be either an absolute path or a path relative to the [`cwd` option](#cwd).

#### addExecPath

Type: `boolean`\
Default: `true`

Whether to push the current Node.js executable's directory ([`execPath`](#execpath) option) to the front of PATH.

#### preferLocal

Type: `boolean`\
Default: `true`

Whether to push the locally installed binaries' directory to the front of PATH.

#### path

Type: `string`\
Default: [`PATH`](https://github.com/sindresorhus/path-key)

The PATH to be appended.

Set it to an empty string to exclude the default PATH.

Only available with [`npmRunPath()`](#npmrunpathoptions), not [`npmRunPathEnv()`](#npmrunpathenvoptions).

#### env

Type: `object`\
Default: [`process.env`](https://nodejs.org/api/process.html#processenv)

Accepts an object of environment variables, like `process.env`, and modifies the PATH using the correct [PATH key](https://github.com/sindresorhus/path-key). Use this if you're modifying the PATH for use in the `child_process` options.

Only available with [`npmRunPathEnv()`](#npmrunpathenvoptions), not [`npmRunPath()`](#npmrunpathoptions).

## Related

- [npm-run-path-cli](https://github.com/sindresorhus/npm-run-path-cli) - CLI for this module
- [execa](https://github.com/sindresorhus/execa) - Execute a locally installed binary


code:
export interface Options {
	/**
	Throw an error when called more than once.

	@default false
	*/
	readonly throw?: boolean;
}

declare const onetime: {
	/**
	Ensure a function is only called once. When called multiple times it will return the return value from the first call.

	@param fn - Function that should only be called once.
	@returns A function that only calls `fn` once.

	@example
	```
	import onetime from 'onetime';

	let index = 0;

	const foo = onetime(() => ++index);

	foo(); //=> 1
	foo(); //=> 1
	foo(); //=> 1

	onetime.callCount(foo); //=> 3
	```
	*/
	<ArgumentsType extends unknown[], ReturnType>(
		fn: (...arguments: ArgumentsType) => ReturnType,
		options?: Options
	): (...arguments: ArgumentsType) => ReturnType;

	/**
	Get the number of times `fn` has been called.

	@param fn - Function to get call count from.
	@returns A number representing how many times `fn` has been called.

	@example
	```
	import onetime from 'onetime';

	const foo = onetime(() => {});
	foo();
	foo();
	foo();

	console.log(onetime.callCount(foo));
	//=> 3
	```
	*/
	callCount(fn: (...arguments: any[]) => unknown): number;
};

export default onetime;


code:
import mimicFunction from 'mimic-fn';

const calledFunctions = new WeakMap();

const onetime = (function_, options = {}) => {
	if (typeof function_ !== 'function') {
		throw new TypeError('Expected a function');
	}

	let returnValue;
	let callCount = 0;
	const functionName = function_.displayName || function_.name || '<anonymous>';

	const onetime = function (...arguments_) {
		calledFunctions.set(onetime, ++callCount);

		if (callCount === 1) {
			returnValue = function_.apply(this, arguments_);
			function_ = null;
		} else if (options.throw === true) {
			throw new Error(`Function \`${functionName}\` can only be called once`);
		}

		return returnValue;
	};

	mimicFunction(onetime, function_);
	calledFunctions.set(onetime, callCount);

	return onetime;
};

onetime.callCount = function_ => {
	if (!calledFunctions.has(function_)) {
		throw new Error(`The given function \`${function_.name}\` is not wrapped by the \`onetime\` package`);
	}

	return calledFunctions.get(function_);
};

export default onetime;


onetime

description: Ensure a function is only called once

repository: sindresorhus/onetime

keywords: once function one onetime func fn single call called prevent


code:
# onetime

> Ensure a function is only called once

When called multiple times it will return the return value from the first call.

*Unlike the module [once](https://github.com/isaacs/once), this one isn't naughty and extending `Function.prototype`.*

## Install

```
$ npm install onetime
```

## Usage

```js
import onetime from 'onetime';

let index = 0;

const foo = onetime(() => ++index);

foo(); //=> 1
foo(); //=> 1
foo(); //=> 1

onetime.callCount(foo); //=> 3
```

```js
import onetime from 'onetime';

const foo = onetime(() => {}, {throw: true});

foo();

foo();
//=> Error: Function `foo` can only be called once
```

## API

### onetime(fn, options?)

Returns a function that only calls `fn` once.

#### fn

Type: `Function`

Function that should only be called once.

#### options

Type: `object`

##### throw

Type: `boolean`\
Default: `false`

Throw an error when called more than once.

### onetime.callCount(fn)

Returns a number representing how many times `fn` has been called.

Note: It throws an error if you pass in a function that is not wrapped by `onetime`.

```js
import onetime from 'onetime';

const foo = onetime(() => {});

foo();
foo();
foo();

console.log(onetime.callCount(foo));
//=> 3
```

#### fn

Type: `Function`

Function to get call count from.

## onetime for enterprise

Available as part of the Tidelift Subscription.

The maintainers of onetime and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-onetime?utm_source=npm-onetime&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)


code:
export interface Options {
	/**
	Use a custom environment variables object.

	Default: [`process.env`](https://nodejs.org/api/process.html#process_process_env).
	*/
	readonly env?: Record<string, string | undefined>;

	/**
	Get the PATH key for a specific platform.

	Default: [`process.platform`](https://nodejs.org/api/process.html#process_process_platform).
	*/
	readonly platform?: NodeJS.Platform;
}

/**
Get the [PATH](https://en.wikipedia.org/wiki/PATH_(variable)) environment variable key cross-platform.

@example
```
import pathKey from 'path-key';

const key = pathKey();
//=> 'PATH'

const PATH = process.env[key];
//=> '/usr/local/bin:/usr/bin:/bin'
```
*/
export default function pathKey(options?: Options): string;


code:
export default function pathKey(options = {}) {
	const {
		env = process.env,
		platform = process.platform
	} = options;

	if (platform !== 'win32') {
		return 'PATH';
	}

	return Object.keys(env).reverse().find(key => key.toUpperCase() === 'PATH') || 'Path';
}


path-key

description: Get the PATH environment variable key cross-platform

repository: sindresorhus/path-key

keywords: path key environment env variable get cross-platform windows