It appears you have a well-structured Git repository with various files, including SVG icons and HTML documents. Here's a brief overview:

This commit is contained in:
2025-06-11 09:05:15 +02:00
parent 36c2466e53
commit 6d6aa954dd
15556 changed files with 1076330 additions and 1 deletions

View File

@ -0,0 +1,2 @@
#!/usr/bin/env node
export {};

View File

@ -0,0 +1,232 @@
#!/usr/bin/env node
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const yargs_1 = __importDefault(require("yargs"));
const helpers_1 = require("yargs/helpers");
const defaults = __importStar(require("../src/defaults"));
const index_1 = __importDefault(require("../src/index"));
const epilogue_1 = require("./epilogue");
// Clean-up arguments (yargs expects only the arguments after the program name)
const cleanArgs = (0, helpers_1.hideBin)(process.argv);
// Find argument separator (double dash)
const argsSepIdx = cleanArgs.findIndex((arg) => arg === '--');
// Arguments before separator
const argsBeforeSep = argsSepIdx >= 0 ? cleanArgs.slice(0, argsSepIdx) : cleanArgs;
// Arguments after separator
const argsAfterSep = argsSepIdx >= 0 ? cleanArgs.slice(argsSepIdx + 1) : [];
const args = (0, yargs_1.default)(argsBeforeSep)
.usage('$0 [options] <command ...>')
.help('h')
.alias('h', 'help')
.version()
.alias('version', 'v')
.alias('version', 'V')
// TODO: Add some tests for this.
.env('CONCURRENTLY')
.options({
// General
'max-processes': {
alias: 'm',
describe: 'How many processes should run at once.\n' +
'New processes only spawn after all restart tries of a process.\n' +
'Exact number or a percent of CPUs available (for example "50%")',
type: 'string',
},
names: {
alias: 'n',
describe: 'List of custom names to be used in prefix template.\n' +
'Example names: "main,browser,server"',
type: 'string',
},
'name-separator': {
describe: 'The character to split <names> on. Example usage:\n' +
'-n "styles|scripts|server" --name-separator "|"',
default: defaults.nameSeparator,
},
success: {
alias: 's',
describe: 'Which command(s) must exit with code 0 in order for concurrently exit with ' +
'code 0 too. Options are:\n' +
'- "first" for the first command to exit;\n' +
'- "last" for the last command to exit;\n' +
'- "all" for all commands;\n' +
// Note: not a typo. Multiple commands can have the same name.
'- "command-{name}"/"command-{index}" for the commands with that name or index;\n' +
'- "!command-{name}"/"!command-{index}" for all commands but the ones with that ' +
'name or index.\n',
default: defaults.success,
},
raw: {
alias: 'r',
describe: 'Output only raw output of processes, disables prettifying ' +
'and concurrently coloring.',
type: 'boolean',
},
// This one is provided for free. Chalk reads this itself and removes colors.
// https://www.npmjs.com/package/chalk#chalksupportscolor
'no-color': {
describe: 'Disables colors from logging',
type: 'boolean',
},
hide: {
describe: 'Comma-separated list of processes to hide the output.\n' +
'The processes can be identified by their name or index.',
default: defaults.hide,
type: 'string',
},
group: {
alias: 'g',
describe: 'Order the output as if the commands were run sequentially.',
type: 'boolean',
},
timings: {
describe: 'Show timing information for all processes.',
type: 'boolean',
default: defaults.timings,
},
'passthrough-arguments': {
alias: 'P',
describe: 'Passthrough additional arguments to commands (accessible via placeholders) ' +
'instead of treating them as commands.',
type: 'boolean',
default: defaults.passthroughArguments,
},
// Kill others
'kill-others': {
alias: 'k',
describe: 'Kill other processes if one exits or dies.',
type: 'boolean',
},
'kill-others-on-fail': {
describe: 'Kill other processes if one exits with non zero status code.',
type: 'boolean',
},
'kill-signal': {
alias: 'ks',
describe: 'Signal to send to other processes if one exits or dies. (SIGTERM/SIGKILL, defaults to SIGTERM)',
type: 'string',
default: defaults.killSignal,
},
// Prefix
prefix: {
alias: 'p',
describe: 'Prefix used in logging for each process.\n' +
'Possible values: index, pid, time, command, name, none, or a template. ' +
'Example template: "{time}-{pid}"',
defaultDescription: 'index or name (when --names is set)',
type: 'string',
},
'prefix-colors': {
alias: 'c',
describe: 'Comma-separated list of chalk colors to use on prefixes. ' +
'If there are more commands than colors, the last color will be repeated.\n' +
'- Available modifiers: reset, bold, dim, italic, underline, inverse, hidden, strikethrough\n' +
'- Available colors: black, red, green, yellow, blue, magenta, cyan, white, gray, \n' +
'any hex values for colors (e.g. #23de43) or auto for an automatically picked color\n' +
'- Available background colors: bgBlack, bgRed, bgGreen, bgYellow, bgBlue, bgMagenta, bgCyan, bgWhite\n' +
'See https://www.npmjs.com/package/chalk for more information.',
default: defaults.prefixColors,
type: 'string',
},
'prefix-length': {
alias: 'l',
describe: 'Limit how many characters of the command is displayed in prefix. ' +
'The option can be used to shorten the prefix when it is set to "command"',
default: defaults.prefixLength,
type: 'number',
},
'timestamp-format': {
alias: 't',
describe: 'Specify the timestamp in moment/date-fns format.',
default: defaults.timestampFormat,
type: 'string',
},
// Restarting
'restart-tries': {
describe: 'How many times a process that died should restart.\n' +
'Negative numbers will make the process restart forever.',
default: defaults.restartTries,
type: 'number',
},
'restart-after': {
describe: 'Delay time to respawn the process, in milliseconds.',
default: defaults.restartDelay,
type: 'number',
},
// Input
'handle-input': {
alias: 'i',
describe: 'Whether input should be forwarded to the child processes. ' +
'See examples for more information.',
type: 'boolean',
},
'default-input-target': {
default: defaults.defaultInputTarget,
describe: 'Identifier for child process to which input on stdin ' +
'should be sent if not specified at start of input.\n' +
'Can be either the index or the name of the process.',
},
})
.group(['m', 'n', 'name-separator', 's', 'r', 'no-color', 'hide', 'g', 'timings', 'P'], 'General')
.group(['p', 'c', 'l', 't'], 'Prefix styling')
.group(['i', 'default-input-target'], 'Input handling')
.group(['k', 'kill-others-on-fail', 'kill-signal'], 'Killing other processes')
.group(['restart-tries', 'restart-after'], 'Restarting')
.epilogue(epilogue_1.epilogue)
.parseSync();
// Get names of commands by the specified separator
const names = (args.names || '').split(args.nameSeparator);
// If "passthrough-arguments" is disabled, treat additional arguments as commands
const commands = args.passthroughArguments ? args._ : [...args._, ...argsAfterSep];
(0, index_1.default)(commands.map((command, index) => ({
command: String(command),
name: names[index],
})), {
handleInput: args.handleInput,
defaultInputTarget: args.defaultInputTarget,
killOthers: args.killOthers
? ['success', 'failure']
: args.killOthersOnFail
? ['failure']
: [],
killSignal: args.killSignal,
maxProcesses: args.maxProcesses,
raw: args.raw,
hide: args.hide.split(','),
group: args.group,
prefix: args.prefix,
prefixColors: args.prefixColors.split(','),
prefixLength: args.prefixLength,
restartDelay: args.restartAfter,
restartTries: args.restartTries,
successCondition: args.success,
timestampFormat: args.timestampFormat,
timings: args.timings,
additionalArguments: args.passthroughArguments ? argsAfterSep : undefined,
}).result.then(() => process.exit(0), () => process.exit(1));

View File

@ -0,0 +1 @@
export declare const epilogue: string;

90
backend/node_modules/concurrently/dist/bin/epilogue.js generated vendored Normal file
View File

@ -0,0 +1,90 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.epilogue = void 0;
// Add new examples here.
// Always start with `$ $0` so that it a) symbolizes a command line; and b) $0 gets replaced by the binary name uniformly.
const examples = [
{
description: 'Output nothing more than stdout+stderr of child processes',
example: '$ $0 --raw "npm run watch-less" "npm run watch-js"',
},
{
description: 'Normal output but without colors e.g. when logging to file',
example: '$ $0 --no-color "grunt watch" "http-server" > log',
},
{
description: 'Custom prefix',
example: '$ $0 --prefix "{time}-{pid}" "npm run watch" "http-server"',
},
{
description: 'Custom names and colored prefixes',
example: '$ $0 --names "HTTP,WATCH" -c "bgBlue.bold,bgMagenta.bold" "http-server" "npm run watch"',
},
{
description: 'Auto varying colored prefixes',
example: '$ $0 -c "auto" "npm run watch" "http-server"',
},
{
description: 'Mixing auto and manual colored prefixes',
example: '$ $0 -c "red,auto" "npm run watch" "http-server" "echo hello"',
},
{
description: 'Configuring via environment variables with CONCURRENTLY_ prefix',
example: '$ CONCURRENTLY_RAW=true CONCURRENTLY_KILL_OTHERS=true $0 "echo hello" "echo world"',
},
{
description: 'Send input to default',
example: [
'$ $0 --handle-input "nodemon" "npm run watch-js"',
'rs # Sends rs command to nodemon process',
].join('\n'),
},
{
description: 'Send input to specific child identified by index',
example: ['$ $0 --handle-input "npm run watch-js" nodemon', '1:rs'].join('\n'),
},
{
description: 'Send input to specific child identified by name',
example: ['$ $0 --handle-input -n js,srv "npm run watch-js" nodemon', 'srv:rs'].join('\n'),
},
{
description: 'Shortened NPM run commands',
example: '$ $0 npm:watch-node npm:watch-js npm:watch-css',
},
{
description: 'Shortened NPM run command with wildcard (make sure to wrap it in quotes!)',
example: '$ $0 "npm:watch-*"',
},
{
description: 'Exclude patterns so that between "lint:js" and "lint:fix:js", only "lint:js" is ran',
example: '$ $0 "npm:*(!fix)"',
},
{
description: "Passthrough some additional arguments via '{<number>}' placeholder",
example: '$ $0 -P "echo {1}" -- foo',
},
{
description: "Passthrough all additional arguments via '{@}' placeholder",
example: '$ $0 -P "npm:dev-* -- {@}" -- --watch --noEmit',
},
{
description: "Passthrough all additional arguments combined via '{*}' placeholder",
example: '$ $0 -P "npm:dev-* -- {*}" -- --watch --noEmit',
},
];
const examplesString = examples
.map(({ example, description }) => [
` - ${description}`,
example
.split('\n')
.map((line) => ` ${line}`)
.join('\n'),
].join('\n\n'))
.join('\n\n');
exports.epilogue = `
Examples:
${examplesString}
For more details, visit https://github.com/open-cli-tools/concurrently
`;

View File

@ -0,0 +1,19 @@
import { CommandInfo } from '../command';
/**
* A command parser encapsulates a specific logic for mapping `CommandInfo` objects
* into another `CommandInfo`.
*
* A prime example is turning an abstract `npm:foo` into `npm run foo`, but it could also turn
* the prefix color of a command brighter, or maybe even prefixing each command with `time(1)`.
*/
export interface CommandParser {
/**
* Parses `commandInfo` and returns one or more `CommandInfo`s.
*
* Returning multiple `CommandInfo` is used when there are multiple possibilities of commands to
* run given the original input.
* An example of this is when the command contains a wildcard and it must be expanded into all
* viable options so that the consumer can decide which ones to run.
*/
parse(commandInfo: CommandInfo): CommandInfo | CommandInfo[];
}

View File

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@ -0,0 +1,17 @@
import { CommandInfo } from '../command';
import { CommandParser } from './command-parser';
/**
* Replace placeholders with additional arguments.
*/
export declare class ExpandArguments implements CommandParser {
private readonly additionalArguments;
constructor(additionalArguments: string[]);
parse(commandInfo: CommandInfo): {
command: string;
name: string;
env?: Record<string, unknown> | undefined;
cwd?: string | undefined;
prefixColor?: string | undefined;
raw?: boolean | undefined;
};
}

View File

@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExpandArguments = void 0;
const shell_quote_1 = require("shell-quote");
/**
* Replace placeholders with additional arguments.
*/
class ExpandArguments {
constructor(additionalArguments) {
this.additionalArguments = additionalArguments;
}
parse(commandInfo) {
const command = commandInfo.command.replace(/\\?\{([@*]|[1-9][0-9]*)\}/g, (match, placeholderTarget) => {
// Don't replace the placeholder if it is escaped by a backslash.
if (match.startsWith('\\')) {
return match.slice(1);
}
// Replace numeric placeholder if value exists in additional arguments.
if (!isNaN(placeholderTarget) &&
placeholderTarget <= this.additionalArguments.length) {
return (0, shell_quote_1.quote)([this.additionalArguments[placeholderTarget - 1]]);
}
// Replace all arguments placeholder.
if (placeholderTarget === '@') {
return (0, shell_quote_1.quote)(this.additionalArguments);
}
// Replace combined arguments placeholder.
if (placeholderTarget === '*') {
return (0, shell_quote_1.quote)([this.additionalArguments.join(' ')]);
}
// Replace placeholder with empty string
// if value doesn't exist in additional arguments.
return '';
});
return { ...commandInfo, command };
}
}
exports.ExpandArguments = ExpandArguments;

View File

@ -0,0 +1,8 @@
import { CommandInfo } from '../command';
import { CommandParser } from './command-parser';
/**
* Expands commands prefixed with `npm:`, `yarn:`, `pnpm:`, or `bun:` into the full version `npm run <command>` and so on.
*/
export declare class ExpandNpmShortcut implements CommandParser {
parse(commandInfo: CommandInfo): CommandInfo;
}

View File

@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExpandNpmShortcut = void 0;
/**
* Expands commands prefixed with `npm:`, `yarn:`, `pnpm:`, or `bun:` into the full version `npm run <command>` and so on.
*/
class ExpandNpmShortcut {
parse(commandInfo) {
const [, npmCmd, cmdName, args] = commandInfo.command.match(/^(npm|yarn|pnpm|bun):(\S+)(.*)/) || [];
if (!cmdName) {
return commandInfo;
}
return {
...commandInfo,
name: commandInfo.name || cmdName,
command: `${npmCmd} run ${cmdName}${args}`,
};
}
}
exports.ExpandNpmShortcut = ExpandNpmShortcut;

View File

@ -0,0 +1,13 @@
import { CommandInfo } from '../command';
import { CommandParser } from './command-parser';
/**
* Finds wildcards in npm/yarn/pnpm/bun run commands and replaces them with all matching scripts in the
* `package.json` file of the current directory.
*/
export declare class ExpandNpmWildcard implements CommandParser {
private readonly readPackage;
static readPackage(): any;
private scripts?;
constructor(readPackage?: typeof ExpandNpmWildcard.readPackage);
parse(commandInfo: CommandInfo): CommandInfo | CommandInfo[];
}

View File

@ -0,0 +1,68 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExpandNpmWildcard = void 0;
const fs_1 = __importDefault(require("fs"));
const lodash_1 = __importDefault(require("lodash"));
const OMISSION = /\(!([^)]+)\)/;
/**
* Finds wildcards in npm/yarn/pnpm/bun run commands and replaces them with all matching scripts in the
* `package.json` file of the current directory.
*/
class ExpandNpmWildcard {
static readPackage() {
try {
const json = fs_1.default.readFileSync('package.json', { encoding: 'utf-8' });
return JSON.parse(json);
}
catch (e) {
return {};
}
}
constructor(readPackage = ExpandNpmWildcard.readPackage) {
this.readPackage = readPackage;
}
parse(commandInfo) {
const [, npmCmd, cmdName, args] = commandInfo.command.match(/(npm|yarn|pnpm|bun) run (\S+)([^&]*)/) || [];
const wildcardPosition = (cmdName || '').indexOf('*');
// If the regex didn't match an npm script, or it has no wildcard,
// then we have nothing to do here
if (!cmdName || wildcardPosition === -1) {
return commandInfo;
}
if (!this.scripts) {
this.scripts = Object.keys(this.readPackage().scripts || {});
}
const omissionRegex = cmdName.match(OMISSION);
const cmdNameSansOmission = cmdName.replace(OMISSION, '');
const preWildcard = lodash_1.default.escapeRegExp(cmdNameSansOmission.slice(0, wildcardPosition));
const postWildcard = lodash_1.default.escapeRegExp(cmdNameSansOmission.slice(wildcardPosition + 1));
const wildcardRegex = new RegExp(`^${preWildcard}(.*?)${postWildcard}$`);
// If 'commandInfo.name' doesn't match 'cmdName', this means a custom name
// has been specified and thus becomes the prefix (as described in the README).
const prefix = commandInfo.name !== cmdName ? commandInfo.name : '';
return this.scripts
.map((script) => {
const match = script.match(wildcardRegex);
if (omissionRegex) {
const toOmit = script.match(new RegExp(omissionRegex[1]));
if (toOmit) {
return;
}
}
if (match) {
return {
...commandInfo,
command: `${npmCmd} run ${script}${args}`,
// Will use an empty command name if no prefix has been specified and
// the wildcard match is empty, e.g. if `npm:watch-*` matches `npm run watch-`.
name: prefix + match[1],
};
}
})
.filter((commandInfo) => !!commandInfo);
}
}
exports.ExpandNpmWildcard = ExpandNpmWildcard;

View File

@ -0,0 +1,15 @@
import { CommandInfo } from '../command';
import { CommandParser } from './command-parser';
/**
* Strips quotes around commands so that they can run on the current shell.
*/
export declare class StripQuotes implements CommandParser {
parse(commandInfo: CommandInfo): {
command: string;
name: string;
env?: Record<string, unknown> | undefined;
cwd?: string | undefined;
prefixColor?: string | undefined;
raw?: boolean | undefined;
};
}

View File

@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StripQuotes = void 0;
/**
* Strips quotes around commands so that they can run on the current shell.
*/
class StripQuotes {
parse(commandInfo) {
let { command } = commandInfo;
// Removes the quotes surrounding a command.
if (/^"(.+?)"$/.test(command) || /^'(.+?)'$/.test(command)) {
command = command.slice(1, command.length - 1);
}
return { ...commandInfo, command };
}
}
exports.StripQuotes = StripQuotes;

121
backend/node_modules/concurrently/dist/src/command.d.ts generated vendored Normal file
View File

@ -0,0 +1,121 @@
/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
import { ChildProcess as BaseChildProcess, SpawnOptions } from 'child_process';
import * as Rx from 'rxjs';
import { EventEmitter, Writable } from 'stream';
/**
* Identifier for a command; if string, it's the command's name, if number, it's the index.
*/
export type CommandIdentifier = string | number;
export interface CommandInfo {
/**
* Command's name.
*/
name: string;
/**
* Which command line the command has.
*/
command: string;
/**
* Which environment variables should the spawned process have.
*/
env?: Record<string, unknown>;
/**
* The current working directory of the process when spawned.
*/
cwd?: string;
/**
* Color to use on prefix of the command.
*/
prefixColor?: string;
/**
* Output command in raw format.
*/
raw?: boolean;
}
export interface CloseEvent {
command: CommandInfo;
/**
* The command's index among all commands ran.
*/
index: number;
/**
* Whether the command exited because it was killed.
*/
killed: boolean;
/**
* The exit code or signal for the command.
*/
exitCode: string | number;
timings: {
startDate: Date;
endDate: Date;
durationSeconds: number;
};
}
export interface TimerEvent {
startDate: Date;
endDate?: Date;
}
/**
* Subtype of NodeJS's child_process including only what's actually needed for a command to work.
*/
export type ChildProcess = EventEmitter & Pick<BaseChildProcess, 'pid' | 'stdin' | 'stdout' | 'stderr'>;
/**
* Interface for a function that must kill the process with `pid`, optionally sending `signal` to it.
*/
export type KillProcess = (pid: number, signal?: string) => void;
/**
* Interface for a function that spawns a command and returns its child process instance.
*/
export type SpawnCommand = (command: string, options: SpawnOptions) => ChildProcess;
export declare class Command implements CommandInfo {
private readonly killProcess;
private readonly spawn;
private readonly spawnOpts;
readonly index: number;
/** @inheritdoc */
readonly name: string;
/** @inheritdoc */
readonly command: string;
/** @inheritdoc */
readonly prefixColor?: string;
/** @inheritdoc */
readonly env: Record<string, unknown>;
/** @inheritdoc */
readonly cwd?: string;
readonly close: Rx.Subject<CloseEvent>;
readonly error: Rx.Subject<unknown>;
readonly stdout: Rx.Subject<Buffer>;
readonly stderr: Rx.Subject<Buffer>;
readonly timer: Rx.Subject<TimerEvent>;
process?: ChildProcess;
stdin?: Writable;
pid?: number;
killed: boolean;
exited: boolean;
/** @deprecated */
get killable(): boolean;
constructor({ index, name, command, prefixColor, env, cwd }: CommandInfo & {
index: number;
}, spawnOpts: SpawnOptions, spawn: SpawnCommand, killProcess: KillProcess);
/**
* Starts this command, piping output, error and close events onto the corresponding observables.
*/
start(): void;
/**
* Kills this command, optionally specifying a signal to send to it.
*/
kill(code?: string): void;
/**
* Detects whether a command can be killed.
*
* Also works as a type guard on the input `command`.
*/
static canKill(command: Command): command is Command & {
pid: number;
process: ChildProcess;
};
}

117
backend/node_modules/concurrently/dist/src/command.js generated vendored Normal file
View File

@ -0,0 +1,117 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Command = void 0;
const Rx = __importStar(require("rxjs"));
class Command {
/** @deprecated */
get killable() {
return Command.canKill(this);
}
constructor({ index, name, command, prefixColor, env, cwd }, spawnOpts, spawn, killProcess) {
this.close = new Rx.Subject();
this.error = new Rx.Subject();
this.stdout = new Rx.Subject();
this.stderr = new Rx.Subject();
this.timer = new Rx.Subject();
this.killed = false;
this.exited = false;
this.index = index;
this.name = name;
this.command = command;
this.prefixColor = prefixColor;
this.env = env || {};
this.cwd = cwd;
this.killProcess = killProcess;
this.spawn = spawn;
this.spawnOpts = spawnOpts;
}
/**
* Starts this command, piping output, error and close events onto the corresponding observables.
*/
start() {
const child = this.spawn(this.command, this.spawnOpts);
this.process = child;
this.pid = child.pid;
const startDate = new Date(Date.now());
const highResStartTime = process.hrtime();
this.timer.next({ startDate });
Rx.fromEvent(child, 'error').subscribe((event) => {
this.process = undefined;
const endDate = new Date(Date.now());
this.timer.next({ startDate, endDate });
this.error.next(event);
});
Rx.fromEvent(child, 'close')
.pipe(Rx.map((event) => event))
.subscribe(([exitCode, signal]) => {
this.process = undefined;
this.exited = true;
const endDate = new Date(Date.now());
this.timer.next({ startDate, endDate });
const [durationSeconds, durationNanoSeconds] = process.hrtime(highResStartTime);
this.close.next({
command: this,
index: this.index,
exitCode: exitCode ?? String(signal),
killed: this.killed,
timings: {
startDate,
endDate,
durationSeconds: durationSeconds + durationNanoSeconds / 1e9,
},
});
});
child.stdout &&
pipeTo(Rx.fromEvent(child.stdout, 'data').pipe(Rx.map((event) => event)), this.stdout);
child.stderr &&
pipeTo(Rx.fromEvent(child.stderr, 'data').pipe(Rx.map((event) => event)), this.stderr);
this.stdin = child.stdin || undefined;
}
/**
* Kills this command, optionally specifying a signal to send to it.
*/
kill(code) {
if (Command.canKill(this)) {
this.killed = true;
this.killProcess(this.pid, code);
}
}
/**
* Detects whether a command can be killed.
*
* Also works as a type guard on the input `command`.
*/
static canKill(command) {
return !!command.pid && !!command.process;
}
}
exports.Command = Command;
/**
* Pipes all events emitted by `stream` into `subject`.
*/
function pipeTo(stream, subject) {
stream.subscribe((event) => subject.next(event));
}

View File

@ -0,0 +1,40 @@
import * as Rx from 'rxjs';
import { CloseEvent, Command } from './command';
/**
* Defines which command(s) in a list must exit successfully (with an exit code of `0`):
*
* - `first`: only the first specified command;
* - `last`: only the last specified command;
* - `all`: all commands.
* - `command-{name|index}`: only the commands with the specified names or index.
* - `!command-{name|index}`: all commands but the ones with the specified names or index.
*/
export type SuccessCondition = 'first' | 'last' | 'all' | `command-${string | number}` | `!command-${string | number}`;
/**
* Provides logic to determine whether lists of commands ran successfully.
*/
export declare class CompletionListener {
private readonly successCondition;
private readonly scheduler?;
constructor({ successCondition, scheduler, }: {
/**
* How this instance will define that a list of commands ran successfully.
* Defaults to `all`.
*
* @see {SuccessCondition}
*/
successCondition?: SuccessCondition;
/**
* For testing only.
*/
scheduler?: Rx.SchedulerLike;
});
private isSuccess;
/**
* Given a list of commands, wait for all of them to exit and then evaluate their exit codes.
*
* @returns A Promise that resolves if the success condition is met, or rejects otherwise.
*/
listen(commands: Command[]): Promise<CloseEvent[]>;
private emitWithScheduler;
}

View File

@ -0,0 +1,77 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CompletionListener = void 0;
const Rx = __importStar(require("rxjs"));
const operators_1 = require("rxjs/operators");
/**
* Provides logic to determine whether lists of commands ran successfully.
*/
class CompletionListener {
constructor({ successCondition = 'all', scheduler, }) {
this.successCondition = successCondition;
this.scheduler = scheduler;
}
isSuccess(events) {
if (this.successCondition === 'first') {
return events[0].exitCode === 0;
}
else if (this.successCondition === 'last') {
return events[events.length - 1].exitCode === 0;
}
const commandSyntaxMatch = this.successCondition.match(/^!?command-(.+)$/);
if (commandSyntaxMatch == null) {
// If not a `command-` syntax, then it's an 'all' condition or it's treated as such.
return events.every(({ exitCode }) => exitCode === 0);
}
// Check `command-` syntax condition.
// Note that a command's `name` is not necessarily unique,
// in which case all of them must meet the success condition.
const nameOrIndex = commandSyntaxMatch[1];
const targetCommandsEvents = events.filter(({ command, index }) => command.name === nameOrIndex || index === Number(nameOrIndex));
if (this.successCondition.startsWith('!')) {
// All commands except the specified ones must exit succesfully
return events.every((event) => targetCommandsEvents.includes(event) || event.exitCode === 0);
}
// Only the specified commands must exit succesfully
return (targetCommandsEvents.length > 0 &&
targetCommandsEvents.every((event) => event.exitCode === 0));
}
/**
* Given a list of commands, wait for all of them to exit and then evaluate their exit codes.
*
* @returns A Promise that resolves if the success condition is met, or rejects otherwise.
*/
listen(commands) {
const closeStreams = commands.map((command) => command.close);
return Rx.lastValueFrom(Rx.merge(...closeStreams).pipe((0, operators_1.bufferCount)(closeStreams.length), (0, operators_1.switchMap)((exitInfos) => this.isSuccess(exitInfos)
? this.emitWithScheduler(Rx.of(exitInfos))
: this.emitWithScheduler(Rx.throwError(() => exitInfos))), (0, operators_1.take)(1)));
}
emitWithScheduler(input) {
return this.scheduler ? input.pipe(Rx.observeOn(this.scheduler)) : input;
}
}
exports.CompletionListener = CompletionListener;

View File

@ -0,0 +1,110 @@
/// <reference types="node" />
import { Writable } from 'stream';
import { CloseEvent, Command, CommandInfo, KillProcess, SpawnCommand } from './command';
import { SuccessCondition } from './completion-listener';
import { FlowController } from './flow-control/flow-controller';
import { Logger } from './logger';
/**
* A command that is to be passed into `concurrently()`.
* If value is a string, then that's the command's command line.
* Fine grained options can be defined by using the object format.
*/
export type ConcurrentlyCommandInput = string | ({
command: string;
} & Partial<CommandInfo>);
export type ConcurrentlyResult = {
/**
* All commands created and ran by concurrently.
*/
commands: Command[];
/**
* A promise that resolves when concurrently ran successfully according to the specified
* success condition, or reject otherwise.
*
* Both the resolved and rejected value is the list of all command's close events.
*/
result: Promise<CloseEvent[]>;
};
export type ConcurrentlyOptions = {
logger?: Logger;
/**
* Which stream should the commands output be written to.
*/
outputStream?: Writable;
/**
* Whether the output should be ordered as if the commands were run sequentially.
*/
group?: boolean;
/**
* A comma-separated list of chalk colors or a string for available styles listed below to use on prefixes.
* If there are more commands than colors, the last color will be repeated.
*
* Available modifiers:
* - `reset`, `bold`, `dim`, `italic`, `underline`, `inverse`, `hidden`, `strikethrough`
*
* Available colors:
* - `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`, `gray`,
* any hex values for colors (e.g. `#23de43`) or `auto` for an automatically picked color
*
* Available background colors:
* - `bgBlack`, `bgRed`, `bgGreen`, `bgYellow`, `bgBlue`, `bgMagenta`, `bgCyan`, `bgWhite`
*
* @see {@link https://www.npmjs.com/package/chalk} for more information.
*/
prefixColors?: string | string[];
/**
* Maximum number of commands to run at once.
* Exact number or a percent of CPUs available (for example "50%").
*
* If undefined, then all processes will start in parallel.
* Setting this value to 1 will achieve sequential running.
*/
maxProcesses?: number | string;
/**
* Whether commands should be spawned in raw mode.
* Defaults to false.
*/
raw?: boolean;
/**
* The current working directory of commands which didn't specify one.
* Defaults to `process.cwd()`.
*/
cwd?: string;
/**
* @see CompletionListener
*/
successCondition?: SuccessCondition;
/**
* Which flow controllers should be applied on commands spawned by concurrently.
* Defaults to an empty array.
*/
controllers: FlowController[];
/**
* A function that will spawn commands.
* Defaults to the `spawn-command` module.
*/
spawn: SpawnCommand;
/**
* A function that will kill processes.
* Defaults to the `tree-kill` module.
*/
kill: KillProcess;
/**
* Signal to send to killed processes.
*/
killSignal?: string;
/**
* List of additional arguments passed that will get replaced in each command.
* If not defined, no argument replacing will happen.
*
* @see ExpandArguments
*/
additionalArguments?: string[];
};
/**
* Core concurrently functionality -- spawns the given commands concurrently and
* returns the commands themselves + the result according to the specified success condition.
*
* @see CompletionListener
*/
export declare function concurrently(baseCommands: ConcurrentlyCommandInput[], baseOptions?: Partial<ConcurrentlyOptions>): ConcurrentlyResult;

View File

@ -0,0 +1,130 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.concurrently = void 0;
const assert_1 = __importDefault(require("assert"));
const lodash_1 = __importDefault(require("lodash"));
const os_1 = require("os");
const spawn_command_1 = __importDefault(require("spawn-command"));
const tree_kill_1 = __importDefault(require("tree-kill"));
const command_1 = require("./command");
const expand_arguments_1 = require("./command-parser/expand-arguments");
const expand_npm_shortcut_1 = require("./command-parser/expand-npm-shortcut");
const expand_npm_wildcard_1 = require("./command-parser/expand-npm-wildcard");
const strip_quotes_1 = require("./command-parser/strip-quotes");
const completion_listener_1 = require("./completion-listener");
const get_spawn_opts_1 = require("./get-spawn-opts");
const output_writer_1 = require("./output-writer");
const prefix_color_selector_1 = require("./prefix-color-selector");
const defaults = {
spawn: spawn_command_1.default,
kill: tree_kill_1.default,
raw: false,
controllers: [],
cwd: undefined,
};
/**
* Core concurrently functionality -- spawns the given commands concurrently and
* returns the commands themselves + the result according to the specified success condition.
*
* @see CompletionListener
*/
function concurrently(baseCommands, baseOptions) {
assert_1.default.ok(Array.isArray(baseCommands), '[concurrently] commands should be an array');
assert_1.default.notStrictEqual(baseCommands.length, 0, '[concurrently] no commands provided');
const options = lodash_1.default.defaults(baseOptions, defaults);
const prefixColorSelector = new prefix_color_selector_1.PrefixColorSelector(options.prefixColors);
const commandParsers = [
new strip_quotes_1.StripQuotes(),
new expand_npm_shortcut_1.ExpandNpmShortcut(),
new expand_npm_wildcard_1.ExpandNpmWildcard(),
];
if (options.additionalArguments) {
commandParsers.push(new expand_arguments_1.ExpandArguments(options.additionalArguments));
}
let commands = (0, lodash_1.default)(baseCommands)
.map(mapToCommandInfo)
.flatMap((command) => parseCommand(command, commandParsers))
.map((command, index) => {
return new command_1.Command({
index,
prefixColor: prefixColorSelector.getNextColor(),
...command,
}, (0, get_spawn_opts_1.getSpawnOpts)({
raw: command.raw ?? options.raw,
env: command.env,
cwd: command.cwd || options.cwd,
}), options.spawn, options.kill);
})
.value();
const handleResult = options.controllers.reduce(({ commands: prevCommands, onFinishCallbacks }, controller) => {
const { commands, onFinish } = controller.handle(prevCommands);
return {
commands,
onFinishCallbacks: lodash_1.default.concat(onFinishCallbacks, onFinish ? [onFinish] : []),
};
}, { commands, onFinishCallbacks: [] });
commands = handleResult.commands;
if (options.logger && options.outputStream) {
const outputWriter = new output_writer_1.OutputWriter({
outputStream: options.outputStream,
group: !!options.group,
commands,
});
options.logger.output.subscribe(({ command, text }) => outputWriter.write(command, text));
}
const commandsLeft = commands.slice();
const maxProcesses = Math.max(1, (typeof options.maxProcesses === 'string' && options.maxProcesses.endsWith('%')
? Math.round(((0, os_1.cpus)().length * Number(options.maxProcesses.slice(0, -1))) / 100)
: Number(options.maxProcesses)) || commandsLeft.length);
for (let i = 0; i < maxProcesses; i++) {
maybeRunMore(commandsLeft);
}
const result = new completion_listener_1.CompletionListener({ successCondition: options.successCondition })
.listen(commands)
.finally(() => {
handleResult.onFinishCallbacks.forEach((onFinish) => onFinish());
});
return {
result,
commands,
};
}
exports.concurrently = concurrently;
function mapToCommandInfo(command) {
if (typeof command === 'string') {
return mapToCommandInfo({ command });
}
assert_1.default.ok(command.command, '[concurrently] command cannot be empty');
return {
command: command.command,
name: command.name || '',
env: command.env || {},
cwd: command.cwd || '',
...(command.prefixColor
? {
prefixColor: command.prefixColor,
}
: {}),
...(command.raw !== undefined
? {
raw: command.raw,
}
: {}),
};
}
function parseCommand(command, parsers) {
return parsers.reduce((commands, parser) => lodash_1.default.flatMap(commands, (command) => parser.parse(command)), lodash_1.default.castArray(command));
}
function maybeRunMore(commandsLeft) {
const command = commandsLeft.shift();
if (!command) {
return;
}
command.start();
command.close.subscribe(() => {
maybeRunMore(commandsLeft);
});
}

View File

@ -0,0 +1,68 @@
import { SuccessCondition } from './completion-listener';
export declare const defaultInputTarget = 0;
/**
* Whether process.stdin should be forwarded to child processes.
*/
export declare const handleInput = false;
/**
* How many processes to run at once.
*/
export declare const maxProcesses = 0;
/**
* Indices and names of commands whose output are not to be logged.
*/
export declare const hide = "";
/**
* The character to split <names> on.
*/
export declare const nameSeparator = ",";
/**
* Which prefix style to use when logging processes output.
*/
export declare const prefix = "";
/**
* Default prefix color.
* @see https://www.npmjs.com/package/chalk
*/
export declare const prefixColors = "reset";
/**
* How many bytes we'll show on the command prefix.
*/
export declare const prefixLength = 10;
export declare const raw = false;
/**
* Number of attempts of restarting a process, if it exits with non-0 code.
*/
export declare const restartTries = 0;
/**
* How many milliseconds concurrently should wait before restarting a process.
*/
export declare const restartDelay = 0;
/**
* Condition of success for concurrently itself.
*/
export declare const success: SuccessCondition;
/**
* Date format used when logging date/time.
* @see https://date-fns.org/v2.0.1/docs/format
*/
export declare const timestampFormat = "yyyy-MM-dd HH:mm:ss.SSS";
/**
* Current working dir passed as option to spawn command.
* Defaults to process.cwd()
*/
export declare const cwd: string | undefined;
/**
* Whether to show timing information for processes in console output.
*/
export declare const timings = false;
/**
* Passthrough additional arguments to commands (accessible via placeholders) instead of treating them as commands.
*/
export declare const passthroughArguments = false;
/**
* Signal to send to other processes if one exits or dies.
*
* Defaults to OS specific signal. (SIGTERM on Linux/MacOS)
*/
export declare const killSignal: string | undefined;

73
backend/node_modules/concurrently/dist/src/defaults.js generated vendored Normal file
View File

@ -0,0 +1,73 @@
"use strict";
// This file is meant to be a shared place for default configs.
// It's read by the flow controllers, the executable, etc.
// Refer to tests for the meaning of the different possible values.
Object.defineProperty(exports, "__esModule", { value: true });
exports.killSignal = exports.passthroughArguments = exports.timings = exports.cwd = exports.timestampFormat = exports.success = exports.restartDelay = exports.restartTries = exports.raw = exports.prefixLength = exports.prefixColors = exports.prefix = exports.nameSeparator = exports.hide = exports.maxProcesses = exports.handleInput = exports.defaultInputTarget = void 0;
exports.defaultInputTarget = 0;
/**
* Whether process.stdin should be forwarded to child processes.
*/
exports.handleInput = false;
/**
* How many processes to run at once.
*/
exports.maxProcesses = 0;
/**
* Indices and names of commands whose output are not to be logged.
*/
exports.hide = '';
/**
* The character to split <names> on.
*/
exports.nameSeparator = ',';
/**
* Which prefix style to use when logging processes output.
*/
exports.prefix = '';
/**
* Default prefix color.
* @see https://www.npmjs.com/package/chalk
*/
exports.prefixColors = 'reset';
/**
* How many bytes we'll show on the command prefix.
*/
exports.prefixLength = 10;
exports.raw = false;
/**
* Number of attempts of restarting a process, if it exits with non-0 code.
*/
exports.restartTries = 0;
/**
* How many milliseconds concurrently should wait before restarting a process.
*/
exports.restartDelay = 0;
/**
* Condition of success for concurrently itself.
*/
exports.success = 'all';
/**
* Date format used when logging date/time.
* @see https://date-fns.org/v2.0.1/docs/format
*/
exports.timestampFormat = 'yyyy-MM-dd HH:mm:ss.SSS';
/**
* Current working dir passed as option to spawn command.
* Defaults to process.cwd()
*/
exports.cwd = undefined;
/**
* Whether to show timing information for processes in console output.
*/
exports.timings = false;
/**
* Passthrough additional arguments to commands (accessible via placeholders) instead of treating them as commands.
*/
exports.passthroughArguments = false;
/**
* Signal to send to other processes if one exits or dies.
*
* Defaults to OS specific signal. (SIGTERM on Linux/MacOS)
*/
exports.killSignal = undefined;

View File

@ -0,0 +1,13 @@
import { Command } from '../command';
/**
* Interface for a class that controls and/or watches the behavior of commands.
*
* This may include logging their output, creating interactions between them, or changing when they
* actually finish.
*/
export interface FlowController {
handle(commands: Command[]): {
commands: Command[];
onFinish?: () => void;
};
}

View File

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@ -0,0 +1,30 @@
/// <reference types="node" />
import { Readable } from 'stream';
import { Command, CommandIdentifier } from '../command';
import { Logger } from '../logger';
import { FlowController } from './flow-controller';
/**
* Sends input from concurrently through to commands.
*
* Input can start with a command identifier, in which case it will be sent to that specific command.
* For instance, `0:bla` will send `bla` to command at index `0`, and `server:stop` will send `stop`
* to command with name `server`.
*
* If the input doesn't start with a command identifier, it is then always sent to the default target.
*/
export declare class InputHandler implements FlowController {
private readonly logger;
private readonly defaultInputTarget;
private readonly inputStream?;
private readonly pauseInputStreamOnFinish;
constructor({ defaultInputTarget, inputStream, pauseInputStreamOnFinish, logger, }: {
inputStream?: Readable;
logger: Logger;
defaultInputTarget?: CommandIdentifier;
pauseInputStreamOnFinish?: boolean;
});
handle(commands: Command[]): {
commands: Command[];
onFinish?: () => void | undefined;
};
}

View File

@ -0,0 +1,90 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.InputHandler = void 0;
const Rx = __importStar(require("rxjs"));
const operators_1 = require("rxjs/operators");
const defaults = __importStar(require("../defaults"));
/**
* Sends input from concurrently through to commands.
*
* Input can start with a command identifier, in which case it will be sent to that specific command.
* For instance, `0:bla` will send `bla` to command at index `0`, and `server:stop` will send `stop`
* to command with name `server`.
*
* If the input doesn't start with a command identifier, it is then always sent to the default target.
*/
class InputHandler {
constructor({ defaultInputTarget, inputStream, pauseInputStreamOnFinish, logger, }) {
this.logger = logger;
this.defaultInputTarget = defaultInputTarget || defaults.defaultInputTarget;
this.inputStream = inputStream;
this.pauseInputStreamOnFinish = pauseInputStreamOnFinish !== false;
}
handle(commands) {
const { inputStream } = this;
if (!inputStream) {
return { commands };
}
const commandsMap = new Map();
for (const command of commands) {
commandsMap.set(command.index.toString(), command);
commandsMap.set(command.name, command);
}
Rx.fromEvent(inputStream, 'data')
.pipe((0, operators_1.map)((data) => String(data)))
.subscribe((data) => {
let command, input;
const dataParts = data.split(/:(.+)/s);
let target = dataParts[0];
if (dataParts.length > 1 && (command = commandsMap.get(target))) {
input = dataParts[1];
}
else {
// If `target` does not match a registered command,
// fallback to `defaultInputTarget` and forward the whole input data
target = this.defaultInputTarget.toString();
command = commandsMap.get(target);
input = data;
}
if (command && command.stdin) {
command.stdin.write(input);
}
else {
this.logger.logGlobalEvent(`Unable to find command "${target}", or it has no stdin open\n`);
}
});
return {
commands,
onFinish: () => {
if (this.pauseInputStreamOnFinish) {
// https://github.com/kimmobrunfeldt/concurrently/issues/252
inputStream.pause();
}
},
};
}
}
exports.InputHandler = InputHandler;

View File

@ -0,0 +1,17 @@
/// <reference types="node" />
import EventEmitter from 'events';
import { Command } from '../command';
import { FlowController } from './flow-controller';
/**
* Watches the main concurrently process for signals and sends the same signal down to each spawned
* command.
*/
export declare class KillOnSignal implements FlowController {
private readonly process;
constructor({ process }: {
process: EventEmitter;
});
handle(commands: Command[]): {
commands: Command[];
};
}

View File

@ -0,0 +1,36 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.KillOnSignal = void 0;
const operators_1 = require("rxjs/operators");
/**
* Watches the main concurrently process for signals and sends the same signal down to each spawned
* command.
*/
class KillOnSignal {
constructor({ process }) {
this.process = process;
}
handle(commands) {
let caughtSignal;
['SIGINT', 'SIGTERM', 'SIGHUP'].forEach((signal) => {
this.process.on(signal, () => {
caughtSignal = signal;
commands.forEach((command) => command.kill(signal));
});
});
return {
commands: commands.map((command) => {
const closeStream = command.close.pipe((0, operators_1.map)((exitInfo) => {
const exitCode = caughtSignal === 'SIGINT' ? 0 : exitInfo.exitCode;
return { ...exitInfo, exitCode };
}));
return new Proxy(command, {
get(target, prop) {
return prop === 'close' ? closeStream : target[prop];
},
});
}),
};
}
}
exports.KillOnSignal = KillOnSignal;

View File

@ -0,0 +1,20 @@
import { Command } from '../command';
import { Logger } from '../logger';
import { FlowController } from './flow-controller';
export type ProcessCloseCondition = 'failure' | 'success';
/**
* Sends a SIGTERM signal to all commands when one of the commands exits with a matching condition.
*/
export declare class KillOthers implements FlowController {
private readonly logger;
private readonly conditions;
private readonly killSignal;
constructor({ logger, conditions, killSignal, }: {
logger: Logger;
conditions: ProcessCloseCondition | ProcessCloseCondition[];
killSignal: string | undefined;
});
handle(commands: Command[]): {
commands: Command[];
};
}

View File

@ -0,0 +1,35 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.KillOthers = void 0;
const lodash_1 = __importDefault(require("lodash"));
const operators_1 = require("rxjs/operators");
const command_1 = require("../command");
/**
* Sends a SIGTERM signal to all commands when one of the commands exits with a matching condition.
*/
class KillOthers {
constructor({ logger, conditions, killSignal, }) {
this.logger = logger;
this.conditions = lodash_1.default.castArray(conditions);
this.killSignal = killSignal;
}
handle(commands) {
const conditions = this.conditions.filter((condition) => condition === 'failure' || condition === 'success');
if (!conditions.length) {
return { commands };
}
const closeStates = commands.map((command) => command.close.pipe((0, operators_1.map)(({ exitCode }) => exitCode === 0 ? 'success' : 'failure'), (0, operators_1.filter)((state) => conditions.includes(state))));
closeStates.forEach((closeState) => closeState.subscribe(() => {
const killableCommands = commands.filter((command) => command_1.Command.canKill(command));
if (killableCommands.length) {
this.logger.logGlobalEvent(`Sending ${this.killSignal || 'SIGTERM'} to other processes..`);
killableCommands.forEach((command) => command.kill(this.killSignal));
}
}));
return { commands };
}
}
exports.KillOthers = KillOthers;

View File

@ -0,0 +1,15 @@
import { Command } from '../command';
import { Logger } from '../logger';
import { FlowController } from './flow-controller';
/**
* Logs when commands failed executing, e.g. due to the executable not existing in the system.
*/
export declare class LogError implements FlowController {
private readonly logger;
constructor({ logger }: {
logger: Logger;
});
handle(commands: Command[]): {
commands: Command[];
};
}

View File

@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LogError = void 0;
/**
* Logs when commands failed executing, e.g. due to the executable not existing in the system.
*/
class LogError {
constructor({ logger }) {
this.logger = logger;
}
handle(commands) {
commands.forEach((command) => command.error.subscribe((event) => {
this.logger.logCommandEvent(`Error occurred when executing command: ${command.command}`, command);
const errorText = String(event instanceof Error ? event.stack || event : event);
this.logger.logCommandEvent(errorText, command);
}));
return { commands };
}
}
exports.LogError = LogError;

View File

@ -0,0 +1,15 @@
import { Command } from '../command';
import { Logger } from '../logger';
import { FlowController } from './flow-controller';
/**
* Logs the exit code/signal of commands.
*/
export declare class LogExit implements FlowController {
private readonly logger;
constructor({ logger }: {
logger: Logger;
});
handle(commands: Command[]): {
commands: Command[];
};
}

View File

@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LogExit = void 0;
/**
* Logs the exit code/signal of commands.
*/
class LogExit {
constructor({ logger }) {
this.logger = logger;
}
handle(commands) {
commands.forEach((command) => command.close.subscribe(({ exitCode }) => {
this.logger.logCommandEvent(`${command.command} exited with code ${exitCode}`, command);
}));
return { commands };
}
}
exports.LogExit = LogExit;

View File

@ -0,0 +1,15 @@
import { Command } from '../command';
import { Logger } from '../logger';
import { FlowController } from './flow-controller';
/**
* Logs the stdout and stderr output of commands.
*/
export declare class LogOutput implements FlowController {
private readonly logger;
constructor({ logger }: {
logger: Logger;
});
handle(commands: Command[]): {
commands: Command[];
};
}

View File

@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LogOutput = void 0;
/**
* Logs the stdout and stderr output of commands.
*/
class LogOutput {
constructor({ logger }) {
this.logger = logger;
}
handle(commands) {
commands.forEach((command) => {
command.stdout.subscribe((text) => this.logger.logCommandText(text.toString(), command));
command.stderr.subscribe((text) => this.logger.logCommandText(text.toString(), command));
});
return { commands };
}
}
exports.LogOutput = LogOutput;

View File

@ -0,0 +1,31 @@
import { CloseEvent, Command } from '../command';
import { Logger } from '../logger';
import { FlowController } from './flow-controller';
type TimingInfo = {
name: string;
duration: string;
'exit code': string | number;
killed: boolean;
command: string;
};
/**
* Logs timing information about commands as they start/stop and then a summary when all commands finish.
*/
export declare class LogTimings implements FlowController {
static mapCloseEventToTimingInfo({ command, timings, killed, exitCode, }: CloseEvent): TimingInfo;
private readonly logger?;
private readonly timestampFormat;
constructor({ logger, timestampFormat, }: {
logger?: Logger;
timestampFormat?: string;
});
private printExitInfoTimingTable;
handle(commands: Command[]): {
commands: Command[];
onFinish?: undefined;
} | {
commands: Command[];
onFinish: () => void;
};
}
export {};

View File

@ -0,0 +1,92 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LogTimings = void 0;
const assert = __importStar(require("assert"));
const format_1 = __importDefault(require("date-fns/format"));
const lodash_1 = __importDefault(require("lodash"));
const Rx = __importStar(require("rxjs"));
const operators_1 = require("rxjs/operators");
const defaults = __importStar(require("../defaults"));
/**
* Logs timing information about commands as they start/stop and then a summary when all commands finish.
*/
class LogTimings {
static mapCloseEventToTimingInfo({ command, timings, killed, exitCode, }) {
const readableDurationMs = (timings.endDate.getTime() - timings.startDate.getTime()).toLocaleString();
return {
name: command.name,
duration: readableDurationMs,
'exit code': exitCode,
killed,
command: command.command,
};
}
constructor({ logger, timestampFormat = defaults.timestampFormat, }) {
this.logger = logger;
this.timestampFormat = timestampFormat;
}
printExitInfoTimingTable(exitInfos) {
assert.ok(this.logger);
const exitInfoTable = (0, lodash_1.default)(exitInfos)
.sortBy(({ timings }) => timings.durationSeconds)
.reverse()
.map(LogTimings.mapCloseEventToTimingInfo)
.value();
this.logger.logGlobalEvent('Timings:');
this.logger.logTable(exitInfoTable);
return exitInfos;
}
handle(commands) {
const { logger } = this;
if (!logger) {
return { commands };
}
// individual process timings
commands.forEach((command) => {
command.timer.subscribe(({ startDate, endDate }) => {
if (!endDate) {
const formattedStartDate = (0, format_1.default)(startDate, this.timestampFormat);
logger.logCommandEvent(`${command.command} started at ${formattedStartDate}`, command);
}
else {
const durationMs = endDate.getTime() - startDate.getTime();
const formattedEndDate = (0, format_1.default)(endDate, this.timestampFormat);
logger.logCommandEvent(`${command.command} stopped at ${formattedEndDate} after ${durationMs.toLocaleString()}ms`, command);
}
});
});
// overall summary timings
const closeStreams = commands.map((command) => command.close);
const finished = new Rx.Subject();
const allProcessesClosed = Rx.merge(...closeStreams).pipe((0, operators_1.bufferCount)(closeStreams.length), (0, operators_1.take)(1), (0, operators_1.combineLatestWith)(finished));
allProcessesClosed.subscribe(([exitInfos]) => this.printExitInfoTimingTable(exitInfos));
return { commands, onFinish: () => finished.next() };
}
}
exports.LogTimings = LogTimings;

View File

@ -0,0 +1,22 @@
import * as Rx from 'rxjs';
import { Command } from '../command';
import { Logger } from '../logger';
import { FlowController } from './flow-controller';
/**
* Restarts commands that fail up to a defined number of times.
*/
export declare class RestartProcess implements FlowController {
private readonly logger;
private readonly scheduler?;
readonly delay: number;
readonly tries: number;
constructor({ delay, tries, logger, scheduler, }: {
delay?: number;
tries?: number;
logger: Logger;
scheduler?: Rx.SchedulerLike;
});
handle(commands: Command[]): {
commands: Command[];
};
}

View File

@ -0,0 +1,76 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RestartProcess = void 0;
const Rx = __importStar(require("rxjs"));
const operators_1 = require("rxjs/operators");
const defaults = __importStar(require("../defaults"));
/**
* Restarts commands that fail up to a defined number of times.
*/
class RestartProcess {
constructor({ delay, tries, logger, scheduler, }) {
this.logger = logger;
this.delay = delay != null ? +delay : defaults.restartDelay;
this.tries = tries != null ? +tries : defaults.restartTries;
this.tries = this.tries < 0 ? Infinity : this.tries;
this.scheduler = scheduler;
}
handle(commands) {
if (this.tries === 0) {
return { commands };
}
commands
.map((command) => command.close.pipe((0, operators_1.take)(this.tries), (0, operators_1.takeWhile)(({ exitCode }) => exitCode !== 0)))
.map((failure, index) => Rx.merge(
// Delay the emission (so that the restarts happen on time),
// explicitly telling the subscriber that a restart is needed
failure.pipe((0, operators_1.delay)(this.delay, this.scheduler), (0, operators_1.map)(() => true)),
// Skip the first N emissions (as these would be duplicates of the above),
// meaning it will be empty because of success, or failed all N times,
// and no more restarts should be attempted.
failure.pipe((0, operators_1.skip)(this.tries), (0, operators_1.map)(() => false), (0, operators_1.defaultIfEmpty)(false))).subscribe((restart) => {
const command = commands[index];
if (restart) {
this.logger.logCommandEvent(`${command.command} restarted`, command);
command.start();
}
}));
return {
commands: commands.map((command) => {
const closeStream = command.close.pipe((0, operators_1.filter)(({ exitCode }, emission) => {
// We let all success codes pass, and failures only after restarting won't happen again
return exitCode === 0 || emission >= this.tries;
}));
return new Proxy(command, {
get(target, prop) {
return prop === 'close' ? closeStream : target[prop];
},
});
}),
};
}
}
exports.RestartProcess = RestartProcess;

View File

@ -0,0 +1,34 @@
/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
import { SpawnOptions } from 'child_process';
import supportsColor from 'supports-color';
export declare const getSpawnOpts: ({ colorSupport, cwd, process, raw, env, }: {
/**
* What the color support of the spawned processes should be.
* If set to `false`, then no colors should be output.
*
* Defaults to whatever the terminal's stdout support is.
*/
colorSupport?: false | Pick<supportsColor.supportsColor.Level, "level"> | undefined;
/**
* The NodeJS process.
*/
process?: Pick<NodeJS.Process, "platform" | "cwd" | "env"> | undefined;
/**
* A custom working directory to spawn processes in.
* Defaults to `process.cwd()`.
*/
cwd?: string | undefined;
/**
* Whether to customize the options for spawning processes in raw mode.
* Defaults to false.
*/
raw?: boolean | undefined;
/**
* Map of custom environment variables to include in the spawn options.
*/
env?: Record<string, unknown> | undefined;
}) => SpawnOptions;

View File

@ -0,0 +1,18 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSpawnOpts = void 0;
const supports_color_1 = __importDefault(require("supports-color"));
const getSpawnOpts = ({ colorSupport = supports_color_1.default.stdout, cwd, process = global.process, raw = false, env = {}, }) => ({
cwd: cwd || process.cwd(),
...(raw && { stdio: 'inherit' }),
...(/^win/.test(process.platform) && { detached: false }),
env: {
...(colorSupport ? { FORCE_COLOR: colorSupport.level.toString() } : {}),
...process.env,
...env,
},
});
exports.getSpawnOpts = getSpawnOpts;

74
backend/node_modules/concurrently/dist/src/index.d.ts generated vendored Normal file
View File

@ -0,0 +1,74 @@
/// <reference types="node" />
import { Readable } from 'stream';
import { CloseEvent, Command, CommandIdentifier, TimerEvent } from './command';
import { concurrently, ConcurrentlyCommandInput, ConcurrentlyOptions as BaseConcurrentlyOptions, ConcurrentlyResult } from './concurrently';
import { FlowController } from './flow-control/flow-controller';
import { InputHandler } from './flow-control/input-handler';
import { KillOnSignal } from './flow-control/kill-on-signal';
import { KillOthers, ProcessCloseCondition } from './flow-control/kill-others';
import { LogError } from './flow-control/log-error';
import { LogExit } from './flow-control/log-exit';
import { LogOutput } from './flow-control/log-output';
import { LogTimings } from './flow-control/log-timings';
import { RestartProcess } from './flow-control/restart-process';
import { Logger } from './logger';
export type ConcurrentlyOptions = BaseConcurrentlyOptions & {
/**
* Which command(s) should have their output hidden.
*/
hide?: CommandIdentifier | CommandIdentifier[];
/**
* The prefix format to use when logging a command's output.
* Defaults to the command's index.
*/
prefix?: string;
/**
* How many characters should a prefix have at most, used when the prefix format is `command`.
*/
prefixLength?: number;
/**
* Whether output should be formatted to include prefixes and whether "event" logs will be logged.
*/
raw?: boolean;
/**
* Date format used when logging date/time.
* @see https://date-fns.org/v2.0.1/docs/format
*/
timestampFormat?: string;
defaultInputTarget?: CommandIdentifier;
inputStream?: Readable;
handleInput?: boolean;
pauseInputStreamOnFinish?: boolean;
/**
* How much time in milliseconds to wait before restarting a command.
*
* @see RestartProcess
*/
restartDelay?: number;
/**
* How many times commands should be restarted when they exit with a failure.
*
* @see RestartProcess
*/
restartTries?: number;
/**
* Under which condition(s) should other commands be killed when the first one exits.
*
* @see KillOthers
*/
killOthers?: ProcessCloseCondition | ProcessCloseCondition[];
/**
* Whether to output timing information for processes.
*
* @see LogTimings
*/
timings?: boolean;
/**
* List of additional arguments passed that will get replaced in each command.
* If not defined, no argument replacing will happen.
*/
additionalArguments?: string[];
};
declare const _default: (commands: ConcurrentlyCommandInput[], options?: Partial<ConcurrentlyOptions>) => ConcurrentlyResult;
export default _default;
export { CloseEvent, Command, CommandIdentifier, concurrently, ConcurrentlyCommandInput, ConcurrentlyResult, FlowController, InputHandler, KillOnSignal, KillOthers, LogError, LogExit, Logger, LogOutput, LogTimings, RestartProcess, TimerEvent, };

71
backend/node_modules/concurrently/dist/src/index.js generated vendored Normal file
View File

@ -0,0 +1,71 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RestartProcess = exports.LogTimings = exports.LogOutput = exports.Logger = exports.LogExit = exports.LogError = exports.KillOthers = exports.KillOnSignal = exports.InputHandler = exports.concurrently = exports.Command = void 0;
const command_1 = require("./command");
Object.defineProperty(exports, "Command", { enumerable: true, get: function () { return command_1.Command; } });
const concurrently_1 = require("./concurrently");
Object.defineProperty(exports, "concurrently", { enumerable: true, get: function () { return concurrently_1.concurrently; } });
const input_handler_1 = require("./flow-control/input-handler");
Object.defineProperty(exports, "InputHandler", { enumerable: true, get: function () { return input_handler_1.InputHandler; } });
const kill_on_signal_1 = require("./flow-control/kill-on-signal");
Object.defineProperty(exports, "KillOnSignal", { enumerable: true, get: function () { return kill_on_signal_1.KillOnSignal; } });
const kill_others_1 = require("./flow-control/kill-others");
Object.defineProperty(exports, "KillOthers", { enumerable: true, get: function () { return kill_others_1.KillOthers; } });
const log_error_1 = require("./flow-control/log-error");
Object.defineProperty(exports, "LogError", { enumerable: true, get: function () { return log_error_1.LogError; } });
const log_exit_1 = require("./flow-control/log-exit");
Object.defineProperty(exports, "LogExit", { enumerable: true, get: function () { return log_exit_1.LogExit; } });
const log_output_1 = require("./flow-control/log-output");
Object.defineProperty(exports, "LogOutput", { enumerable: true, get: function () { return log_output_1.LogOutput; } });
const log_timings_1 = require("./flow-control/log-timings");
Object.defineProperty(exports, "LogTimings", { enumerable: true, get: function () { return log_timings_1.LogTimings; } });
const restart_process_1 = require("./flow-control/restart-process");
Object.defineProperty(exports, "RestartProcess", { enumerable: true, get: function () { return restart_process_1.RestartProcess; } });
const logger_1 = require("./logger");
Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return logger_1.Logger; } });
exports.default = (commands, options = {}) => {
const logger = new logger_1.Logger({
hide: options.hide,
prefixFormat: options.prefix,
prefixLength: options.prefixLength,
raw: options.raw,
timestampFormat: options.timestampFormat,
});
return (0, concurrently_1.concurrently)(commands, {
maxProcesses: options.maxProcesses,
raw: options.raw,
successCondition: options.successCondition,
cwd: options.cwd,
logger,
outputStream: options.outputStream || process.stdout,
group: options.group,
controllers: [
new log_error_1.LogError({ logger }),
new log_output_1.LogOutput({ logger }),
new log_exit_1.LogExit({ logger }),
new input_handler_1.InputHandler({
logger,
defaultInputTarget: options.defaultInputTarget,
inputStream: options.inputStream || (options.handleInput ? process.stdin : undefined),
pauseInputStreamOnFinish: options.pauseInputStreamOnFinish,
}),
new kill_on_signal_1.KillOnSignal({ process }),
new restart_process_1.RestartProcess({
logger,
delay: options.restartDelay,
tries: options.restartTries,
}),
new kill_others_1.KillOthers({
logger,
conditions: options.killOthers || [],
killSignal: options.killSignal,
}),
new log_timings_1.LogTimings({
logger: options.timings ? logger : undefined,
timestampFormat: options.timestampFormat,
}),
],
prefixColors: options.prefixColors || [],
additionalArguments: options.additionalArguments,
});
};

72
backend/node_modules/concurrently/dist/src/logger.d.ts generated vendored Normal file
View File

@ -0,0 +1,72 @@
import * as Rx from 'rxjs';
import { Command, CommandIdentifier } from './command';
export declare class Logger {
private readonly hide;
private readonly raw;
private readonly prefixFormat?;
private readonly prefixLength;
private readonly timestampFormat;
/**
* Last character emitted.
* If `undefined`, then nothing has been logged yet.
*/
private lastChar?;
/**
* Observable that emits when there's been output logged.
* If `command` is is `undefined`, then the log is for a global event.
*/
readonly output: Rx.Subject<{
command: Command | undefined;
text: string;
}>;
constructor({ hide, prefixFormat, prefixLength, raw, timestampFormat, }: {
/**
* Which command(s) should have their output hidden.
*/
hide?: CommandIdentifier | CommandIdentifier[];
/**
* Whether output should be formatted to include prefixes and whether "event" logs will be
* logged.
*/
raw?: boolean;
/**
* The prefix format to use when logging a command's output.
* Defaults to the command's index.
*/
prefixFormat?: string;
/**
* How many characters should a prefix have at most, used when the prefix format is `command`.
*/
prefixLength?: number;
/**
* Date format used when logging date/time.
* @see https://date-fns.org/v2.0.1/docs/format
*/
timestampFormat?: string;
});
private shortenText;
private getPrefixesFor;
getPrefix(command: Command): string;
colorText(command: Command, text: string): string;
/**
* Logs an event for a command (e.g. start, stop).
*
* If raw mode is on, then nothing is logged.
*/
logCommandEvent(text: string, command: Command): void;
logCommandText(text: string, command: Command): void;
/**
* Logs a global event (e.g. sending signals to processes).
*
* If raw mode is on, then nothing is logged.
*/
logGlobalEvent(text: string): void;
/**
* Logs a table from an input object array, like `console.table`.
*
* Each row is a single input item, and they are presented in the input order.
*/
logTable(tableContents: Record<string, unknown>[]): void;
log(prefix: string, text: string, command?: Command): void;
emit(command: Command | undefined, text: string): void;
}

202
backend/node_modules/concurrently/dist/src/logger.js generated vendored Normal file
View File

@ -0,0 +1,202 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Logger = void 0;
const chalk_1 = __importDefault(require("chalk"));
const format_1 = __importDefault(require("date-fns/format"));
const lodash_1 = __importDefault(require("lodash"));
const Rx = __importStar(require("rxjs"));
const defaults = __importStar(require("./defaults"));
class Logger {
constructor({ hide, prefixFormat, prefixLength, raw = false, timestampFormat, }) {
/**
* Observable that emits when there's been output logged.
* If `command` is is `undefined`, then the log is for a global event.
*/
this.output = new Rx.Subject();
// To avoid empty strings from hiding the output of commands that don't have a name,
// keep in the list of commands to hide only strings with some length.
// This might happen through the CLI when no `--hide` argument is specified, for example.
this.hide = lodash_1.default.castArray(hide)
.filter((name) => name || name === 0)
.map(String);
this.raw = raw;
this.prefixFormat = prefixFormat;
this.prefixLength = prefixLength || defaults.prefixLength;
this.timestampFormat = timestampFormat || defaults.timestampFormat;
}
shortenText(text) {
if (!text || text.length <= this.prefixLength) {
return text;
}
const ellipsis = '..';
const prefixLength = this.prefixLength - ellipsis.length;
const endLength = Math.floor(prefixLength / 2);
const beginningLength = prefixLength - endLength;
const beginnning = text.slice(0, beginningLength);
const end = text.slice(text.length - endLength, text.length);
return beginnning + ellipsis + end;
}
getPrefixesFor(command) {
return {
pid: String(command.pid),
index: String(command.index),
name: command.name,
command: this.shortenText(command.command),
time: (0, format_1.default)(Date.now(), this.timestampFormat),
};
}
getPrefix(command) {
const prefix = this.prefixFormat || (command.name ? 'name' : 'index');
if (prefix === 'none') {
return '';
}
const prefixes = this.getPrefixesFor(command);
if (Object.keys(prefixes).includes(prefix)) {
return `[${prefixes[prefix]}]`;
}
return lodash_1.default.reduce(prefixes, (prev, val, key) => {
const keyRegex = new RegExp(lodash_1.default.escapeRegExp(`{${key}}`), 'g');
return prev.replace(keyRegex, String(val));
}, prefix);
}
colorText(command, text) {
let color;
if (command.prefixColor && command.prefixColor.startsWith('#')) {
color = chalk_1.default.hex(command.prefixColor);
}
else {
const defaultColor = lodash_1.default.get(chalk_1.default, defaults.prefixColors, chalk_1.default.reset);
color = lodash_1.default.get(chalk_1.default, command.prefixColor ?? '', defaultColor);
}
return color(text);
}
/**
* Logs an event for a command (e.g. start, stop).
*
* If raw mode is on, then nothing is logged.
*/
logCommandEvent(text, command) {
if (this.raw) {
return;
}
this.logCommandText(chalk_1.default.reset(text) + '\n', command);
}
logCommandText(text, command) {
if (this.hide.includes(String(command.index)) || this.hide.includes(command.name)) {
return;
}
const prefix = this.colorText(command, this.getPrefix(command));
return this.log(prefix + (prefix ? ' ' : ''), text, command);
}
/**
* Logs a global event (e.g. sending signals to processes).
*
* If raw mode is on, then nothing is logged.
*/
logGlobalEvent(text) {
if (this.raw) {
return;
}
this.log(chalk_1.default.reset('-->') + ' ', chalk_1.default.reset(text) + '\n');
}
/**
* Logs a table from an input object array, like `console.table`.
*
* Each row is a single input item, and they are presented in the input order.
*/
logTable(tableContents) {
// For now, can only print array tables with some content.
if (this.raw || !Array.isArray(tableContents) || !tableContents.length) {
return;
}
let nextColIndex = 0;
const headers = {};
const contentRows = tableContents.map((row) => {
const rowContents = [];
Object.keys(row).forEach((col) => {
if (!headers[col]) {
headers[col] = {
index: nextColIndex++,
length: col.length,
};
}
const colIndex = headers[col].index;
const formattedValue = String(row[col] == null ? '' : row[col]);
// Update the column length in case this rows value is longer than the previous length for the column.
headers[col].length = Math.max(formattedValue.length, headers[col].length);
rowContents[colIndex] = formattedValue;
return rowContents;
});
return rowContents;
});
const headersFormatted = Object.keys(headers).map((header) => header.padEnd(headers[header].length, ' '));
if (!headersFormatted.length) {
// No columns exist.
return;
}
const borderRowFormatted = headersFormatted.map((header) => '─'.padEnd(header.length, '─'));
this.logGlobalEvent(`┌─${borderRowFormatted.join('─┬─')}─┐`);
this.logGlobalEvent(`${headersFormatted.join(' │ ')}`);
this.logGlobalEvent(`├─${borderRowFormatted.join('─┼─')}─┤`);
contentRows.forEach((contentRow) => {
const contentRowFormatted = headersFormatted.map((header, colIndex) => {
// If the table was expanded after this row was processed, it won't have this column.
// Use an empty string in this case.
const col = contentRow[colIndex] || '';
return col.padEnd(header.length, ' ');
});
this.logGlobalEvent(`${contentRowFormatted.join(' │ ')}`);
});
this.logGlobalEvent(`└─${borderRowFormatted.join('─┴─')}─┘`);
}
log(prefix, text, command) {
if (this.raw) {
return this.emit(command, text);
}
// #70 - replace some ANSI code that would impact clearing lines
text = text.replace(/\u2026/g, '...');
const lines = text.split('\n').map((line, index, lines) => {
// First line will write prefix only if we finished the last write with a LF.
// Last line won't write prefix because it should be empty.
if (index === 0 || index === lines.length - 1) {
return line;
}
return prefix + line;
});
if (!this.lastChar || this.lastChar === '\n') {
this.emit(command, prefix);
}
this.lastChar = text[text.length - 1];
this.emit(command, lines.join('\n'));
}
emit(command, text) {
this.output.next({ command, text });
}
}
exports.Logger = Logger;

View File

@ -0,0 +1,19 @@
/// <reference types="node" />
import { Writable } from 'stream';
import { Command } from './command';
/**
* Class responsible for actually writing output onto a writable stream.
*/
export declare class OutputWriter {
private readonly outputStream;
private readonly group;
readonly buffers: string[][];
activeCommandIndex: number;
constructor({ outputStream, group, commands, }: {
outputStream: Writable;
group: boolean;
commands: Command[];
});
write(command: Command | undefined, text: string): void;
private flushBuffer;
}

View File

@ -0,0 +1,71 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OutputWriter = void 0;
const Rx = __importStar(require("rxjs"));
/**
* Class responsible for actually writing output onto a writable stream.
*/
class OutputWriter {
constructor({ outputStream, group, commands, }) {
this.activeCommandIndex = 0;
this.outputStream = outputStream;
this.group = group;
this.buffers = commands.map(() => []);
if (this.group) {
Rx.merge(...commands.map((c) => c.close)).subscribe((command) => {
if (command.index !== this.activeCommandIndex) {
return;
}
for (let i = command.index + 1; i < commands.length; i++) {
this.activeCommandIndex = i;
this.flushBuffer(i);
if (!commands[i].exited) {
break;
}
}
});
}
}
write(command, text) {
if (this.group && command) {
if (command.index <= this.activeCommandIndex) {
this.outputStream.write(text);
}
else {
this.buffers[command.index].push(text);
}
}
else {
// "global" logs (command=null) are output out of order
this.outputStream.write(text);
}
}
flushBuffer(index) {
this.buffers[index].forEach((t) => this.outputStream.write(t));
this.buffers[index] = [];
}
}
exports.OutputWriter = OutputWriter;

View File

@ -0,0 +1,11 @@
import chalk from 'chalk';
export declare class PrefixColorSelector {
private colorGenerator;
constructor(customColors?: string | string[]);
/** A list of colors that are readable in a terminal. */
static get ACCEPTABLE_CONSOLE_COLORS(): ("stderr" | keyof chalk.Chalk | "supportsColor" | "Level" | "Color" | "ForegroundColor" | "BackgroundColor" | "Modifiers")[];
/**
* @returns The given custom colors then a set of acceptable console colors indefinitely.
*/
getNextColor(): string;
}

View File

@ -0,0 +1,93 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PrefixColorSelector = void 0;
function getConsoleColorsWithoutCustomColors(customColors) {
return PrefixColorSelector.ACCEPTABLE_CONSOLE_COLORS.filter(
// Consider the "Bright" variants of colors to be the same as the plain color to avoid similar colors
(color) => !customColors.includes(color.replace(/Bright$/, '')));
}
/**
* Creates a generator that yields an infinite stream of colors.
*/
function* createColorGenerator(customColors) {
// Custom colors should be used as is, except for "auto"
const nextAutoColors = getConsoleColorsWithoutCustomColors(customColors);
let lastColor;
for (const customColor of customColors) {
let currentColor = customColor;
if (currentColor !== 'auto') {
yield currentColor; // Manual color
}
else {
// Find the first auto color that is not the same as the last color
while (currentColor === 'auto' || lastColor === currentColor) {
if (!nextAutoColors.length) {
// There could be more "auto" values than auto colors so this needs to be able to refill
nextAutoColors.push(...PrefixColorSelector.ACCEPTABLE_CONSOLE_COLORS);
}
currentColor = String(nextAutoColors.shift());
}
yield currentColor; // Auto color
}
lastColor = currentColor;
}
const lastCustomColor = customColors[customColors.length - 1] || '';
if (lastCustomColor !== 'auto') {
while (true) {
yield lastCustomColor; // If last custom color was not "auto" then return same color forever, to maintain existing behaviour
}
}
// Finish the initial set(s) of auto colors to avoid repetition
for (const color of nextAutoColors) {
yield color;
}
// Yield an infinite stream of acceptable console colors
//
// If the given custom colors use every ACCEPTABLE_CONSOLE_COLORS except one then there is a chance a color will be repeated,
// however its highly unlikely and low consequence so not worth the extra complexity to account for it
while (true) {
for (const color of PrefixColorSelector.ACCEPTABLE_CONSOLE_COLORS) {
yield color; // Repeat colors forever
}
}
}
class PrefixColorSelector {
constructor(customColors = []) {
const normalizedColors = typeof customColors === 'string' ? [customColors] : customColors;
this.colorGenerator = createColorGenerator(normalizedColors);
}
/** A list of colors that are readable in a terminal. */
static get ACCEPTABLE_CONSOLE_COLORS() {
// Colors picked randomly, can be amended if required
return [
// Prevent duplicates, in case the list becomes significantly large
...new Set([
// Text colors
'cyan',
'yellow',
'greenBright',
'blueBright',
'magentaBright',
'white',
'grey',
'red',
// Background colors
'bgCyan',
'bgYellow',
'bgGreenBright',
'bgBlueBright',
'bgMagenta',
'bgWhiteBright',
'bgGrey',
'bgRed',
]),
];
}
/**
* @returns The given custom colors then a set of acceptable console colors indefinitely.
*/
getNextColor() {
return this.colorGenerator.next().value;
}
}
exports.PrefixColorSelector = PrefixColorSelector;