poku
Version:
🐷 Poku makes testing easy for Node.js, Bun, Deno, and you at the same time.
1,154 lines • 52.2 kB
JavaScript
import { relative, resolve, extname, sep, join, normalize } from "node:path";
import process$1, { cwd as cwd$2, stdout, env, argv, platform, hrtime, exit as exit$1 } from "node:process";
import * as os from "node:os";
import { spawn } from "node:child_process";
import nodeAssert, { AssertionError, deepStrictEqual, deepEqual } from "node:assert";
import nodeAssert$1 from "node:assert/strict";
import { stat, readdir, readFile } from "node:fs/promises";
import { createConnection } from "node:net";
const SAFE_REGEXP = /[.*+{}[\]\\]/g, escapeRegExp = (value) => value.replace(SAFE_REGEXP, "\\$&"), getRuntime = () => typeof Deno < "u" ? "deno" : typeof Bun < "u" ? "bun" : "node", globalScope = globalThis, getSharedState = (name, initial) => {
const stateKey = /* @__PURE__ */ Symbol.for(`@poku/${name}`);
return globalScope[stateKey] === void 0 && (globalScope[stateKey] = initial), globalScope[stateKey];
}, cwd$1 = getSharedState("cwd", cwd$2()), indentation = getSharedState("indentation", {
test: " ",
stdio: " ",
describeDepth: 0,
itDepth: 0
}), results = getSharedState("results", {
passed: 0,
failed: 0,
skipped: 0,
todo: 0
}), BG_CODES = {
white: "\x1B[7m\x1B[1m",
black: "\x1B[40m\x1B[1m",
grey: "\x1B[100m\x1B[1m",
red: "\x1B[41m\x1B[1m",
green: "\x1B[42m\x1B[1m",
yellow: "\x1B[43m\x1B[1m",
blue: "\x1B[44m\x1B[1m",
magenta: "\x1B[45m\x1B[1m",
cyan: "\x1B[46m\x1B[1m",
brightRed: "\x1B[101m\x1B[1m",
brightGreen: "\x1B[102m\x1B[1m",
brightYellow: "\x1B[103m\x1B[1m",
brightBlue: "\x1B[104m\x1B[1m",
brightMagenta: "\x1B[105m\x1B[1m",
brightCyan: "\x1B[106m\x1B[1m"
};
class Formatter {
constructor(text) {
this.parts = "", this.text = text;
}
code(code) {
return this.parts += `\x1B[${code}m`, this;
}
dim() {
return this.parts += "\x1B[2m", this;
}
bold() {
return this.parts += "\x1B[1m", this;
}
underline() {
return this.parts += "\x1B[4m", this;
}
info() {
return this.parts += "\x1B[94m", this;
}
italic() {
return this.parts += "\x1B[3m", this;
}
success() {
return this.parts += "\x1B[32m", this;
}
fail() {
return this.parts += "\x1B[91m", this;
}
gray() {
return this.parts += "\x1B[90m", this;
}
cyan() {
return this.parts += "\x1B[96m", this;
}
bg(color) {
return this.parts += BG_CODES[color], this;
}
[Symbol.toPrimitive]() {
return `${this.parts}${this.text}\x1B[0m`;
}
}
const format = (text) => new Formatter(text), SKIP_MARKER = "\x1B[94m\x1B[1m\u25EF", TODO_MARKER = "\x1B[96m\x1B[1m\u25CF", ANSI_RESET = "\x1B[0m", timeoutMessage = (ms) => `${format(`\u25CF Timeout: test file exceeded ${ms}ms limit`).fail().bold()}`, serialize = (value) => typeof value > "u" || typeof value == "function" || typeof value == "bigint" || typeof value == "symbol" || value instanceof RegExp ? String(value) : Array.isArray(value) ? value.map(serialize) : value instanceof Set ? Array.from(value).map(serialize) : value instanceof Map ? serialize(Object.fromEntries(value)) : value !== null && typeof value == "object" ? Object.fromEntries(
Object.entries(value).map(([key, val]) => [key, serialize(val)])
) : value, parserOutput = (options) => {
const { output, result, debug } = options, pad2 = " ", isDebugOrFailed = debug || !result, outputs = [];
let offset = 0;
for (; offset < output.length; ) {
const nextNewline = output.indexOf(`
`, offset), lineEnd = nextNewline === -1 ? output.length : nextNewline;
if (lineEnd > offset) {
const line = output.substring(offset, lineEnd);
line.includes(SKIP_MARKER) && results.skipped++, line.includes(TODO_MARKER) && results.todo++, line.trim().length > 0 && (isDebugOrFailed || line.indexOf("Exited with code") === -1 && line.includes(ANSI_RESET)) && outputs.push(`${pad2}${line}`);
}
offset = lineEnd + 1;
}
if (outputs.length !== 0)
return outputs;
}, parseResultType = (type) => {
const result = serialize(type);
return typeof result == "string" ? result : JSON.stringify(result, null, 2);
}, charCode = {
colon: 58,
backslash: 92,
space: 32,
openParen: 40,
zero: 48,
nine: 57,
upperA: 65,
upperZ: 90,
lowerA: 97,
lowerZ: 122
}, FILE_PROTOCOL = "file://", FILE_PROTOCOL_LENGTH = FILE_PROTOCOL.length, INTERNAL_PATH = "poku/lib/", isAlpha = (code) => code >= charCode.upperA && code <= charCode.upperZ || code >= charCode.lowerA && code <= charCode.lowerZ, isDigit = (code) => code >= charCode.zero && code <= charCode.nine, findPathStart = (line) => {
const protoIndex = line.indexOf(FILE_PROTOCOL);
if (protoIndex !== -1) return protoIndex + FILE_PROTOCOL_LENGTH;
const slashIndex = line.indexOf("/");
if (slashIndex === 0) return 0;
if (slashIndex > 0 && (line.charCodeAt(slashIndex - 1) === charCode.space || line.charCodeAt(slashIndex - 1) === charCode.openParen))
return slashIndex;
const length = line.length;
for (let i = 0; i < length - 2; i++)
if (isAlpha(line.charCodeAt(i)) && line.charCodeAt(i + 1) === charCode.colon && line.charCodeAt(i + 2) === charCode.backslash)
return i;
return -1;
}, findLineColStart = (line, after) => {
let position = -1;
for (let i = line.length - 1; i > after; i--)
line.charCodeAt(i) === charCode.colon && i + 1 < line.length && isDigit(line.charCodeAt(i + 1)) && (position = i);
return position;
}, findFileFromStack = (stack, options) => {
if (!stack) return "";
const lines = stack.split(`
`), skipInternal = options?.skipInternal;
for (const line of lines) {
if (skipInternal && line.indexOf(INTERNAL_PATH) !== -1) continue;
const pathStart = findPathStart(line);
if (pathStart === -1) continue;
const colonPos = findLineColStart(line, pathStart);
if (colonPos !== -1) {
if (skipInternal) {
const protoIndex = line.indexOf(FILE_PROTOCOL), start = protoIndex !== -1 ? protoIndex : pathStart;
let end = line.length;
for (; end > colonPos && !isDigit(line.charCodeAt(end - 1)); ) end--;
return line.slice(start, end);
}
return line.slice(pathStart, colonPos);
}
}
return "";
}, pad = (num) => String(num).padStart(2, "0"), parseTime = (date) => {
const hours = pad(date.getHours()), minutes = pad(date.getMinutes()), seconds = pad(date.getSeconds());
return `${hours}:${minutes}:${seconds}`;
}, parseTimeToSecs = (milliseconds) => (milliseconds / 1e3).toFixed(2), columns = Math.max((stdout.columns || 50) - 10, 40), hrLine = `
\x1B[2m\x1B[90m${"\u2500".repeat(columns)}\x1B[0m
`, log$1 = (data) => {
stdout.write(`${data}
`);
}, hr = () => {
log$1(hrLine);
}, ARROW_PASS = "\x1B[32m\x1B[1m\u203A\x1B[0m", ARROW_FAIL = "\x1B[91m\x1B[1m\u203A\x1B[0m", getIndent = () => indentation.test.repeat(indentation.describeDepth + indentation.itDepth), errors = getSharedState(
"errors",
[]
), poku$1 = {
onRunStart() {
},
onFileStart() {
},
onDescribeAsTitle(title, options) {
const { background, icon } = options ?? /* @__PURE__ */ Object.create(null), message = `${icon ?? "\u2630"} ${format(title).bold()}`;
log$1(
background ? format(` ${message} `).bg(
typeof background == "string" ? background : "grey"
) : format(message).bold()
);
},
onDescribeStart({ title }) {
if (!title) return;
const indent = getIndent();
log$1(`${indent}${format(`\u25CC ${title}`).bold().dim()}`);
},
onDescribeEnd({ title, duration, success = !0 }) {
const status = success ? "success" : "fail", indent = getIndent();
log$1(
`${indent}${format(`\u25CF ${title}`)[status]().bold()} ${format(
`\u203A ${duration.toFixed(6)}ms`
)[status]().dim()}`
);
},
onItStart({ title }) {
if (!title) return;
const indent = getIndent();
log$1(`${indent}${format(`\u25CC ${title}`).dim()}`);
},
onItEnd({ title, duration, success = !0 }) {
const status = success ? "success" : "fail", indent = getIndent();
log$1(
`${indent}${format(`\u25CF ${title}`)[status]().bold()} ${format(
`\u203A ${duration.toFixed(6)}ms`
)[status]().dim()}`
);
},
onAssertionSuccess({ message }) {
const output = `${getIndent()}${format(`\u2714 ${message}`).success().bold()}`;
log$1(output);
},
onAssertionFailure({ assertOptions: options, error }) {
let preIdentation = getIndent();
const { code, actual, expected, operator } = error, fileRef = findFileFromStack(error.stack, {
skipInternal: !0
}), absolutePath = fileRef.indexOf("file://") === 0 ? fileRef.slice(7) : fileRef, file = relative(resolve(cwd$1), absolutePath);
let message = "";
typeof options.message == "string" ? message = options.message : options.message instanceof Error ? message = options.message.message : typeof options.defaultMessage == "string" && (message = options.defaultMessage);
const output = message?.trim().length > 0 ? format(`\u2718 ${message}`).fail().bold() : format("\u2718 Assertion Error").fail().bold();
if (log$1(`${preIdentation}${output}`), file && log$1(`${format(`${preIdentation} File`).dim()} ${file}`), log$1(`${format(`${preIdentation} Code`).dim()} ${code}`), log$1(`${format(`${preIdentation} Operator`).dim()} ${operator}
`), !options?.hideDiff) {
const splitActual = parseResultType(actual).split(`
`), splitExpected = parseResultType(expected).split(`
`);
log$1(format(`${preIdentation} ${options?.actual ?? "Actual"}:`).dim());
for (const line of splitActual)
log$1(`${preIdentation} ${format(line).fail().bold()}`);
log$1(
`
${preIdentation} ${format(`${options?.expected ?? "Expected"}:`).dim()}`
);
for (const line of splitExpected)
log$1(`${preIdentation} ${format(line).success().bold()}`);
preIdentation = "";
}
options.throw && (console.error(error), hr());
},
onSkipFile({ message }) {
log$1(format(`\u25EF ${message}`).info().bold());
},
onSkipModifier({ message }) {
const indent = getIndent();
log$1(`${indent}${format(`\u25EF ${message}`).info().bold()}`);
},
onTodoModifier({ message }) {
const indent = getIndent();
log$1(`${indent}${format(`\u25CF ${message}`).cyan().bold()}`);
},
onFileResult({ status, path, duration, output }) {
stdout.write(`
`), status ? (log$1(
`${ARROW_PASS} ${format(path.relative).success().underline()} ${format(
`\u203A ${duration.toFixed(6)}ms`
).success().dim()}`
), output && log$1(output)) : log$1(
`${ARROW_FAIL} ${format(path.relative).fail().underline()} ${format(
`\u203A ${duration.toFixed(6)}ms`
).fail().dim()}`
), status || (errors.push({
file: path.relative,
output
}), output && log$1(output));
},
onRunResult() {
if (errors.length !== 0) {
hr(), log$1(
`${format(String(errors.length)).fail().bold()} ${format("test file(s) failed:").bold()}
`
);
for (const i in errors)
if (Object.prototype.hasOwnProperty.call(errors, i)) {
const { file, output } = errors[i], index = +i;
index > 0 && stdout.write(`
`), log$1(
`${format(`${index + 1})`).dim().bold()} ${format(file).underline()}`
), output && log$1(`
${output}`);
}
}
},
onExit({ results: results2, timespan: timespan2 }) {
const success = ` PASS \u203A ${results2.passed} `, failure = ` FAIL \u203A ${results2.failed} `, skips = ` SKIP \u203A ${results2.skipped} `, plans = ` TODO \u203A ${results2.todo} `, inline = results2.skipped === 0 || results2.todo === 0;
let message = "";
inline ? (message += `${format(success).bg("green")} ${format(failure).bg(results2.failed === 0 ? "grey" : "brightRed")}`, results2.skipped && (message += ` ${format(skips).bg("brightBlue")}`), results2.todo && (message += ` ${format(plans).bg("brightBlue")}`)) : (message += `${format(success.trim()).success().bold()}
`, message += results2.failed === 0 ? format(`${failure.trim()}
`).bold() : `${format(failure.trim()).fail().bold()}
`, message += `${format(skips.trim()).info().bold()}
`, message += `${format(plans.trim()).info().bold()}`), hr(), log$1(
`${format(`Start at \u203A ${format(`${parseTime(timespan2.started)}`).bold()}`).dim()}`
), log$1(
`${format("Duration \u203A ").dim()}${format(
`${timespan2.duration.toFixed(6)}ms`
).bold().dim()} ${format(`(\xB1${parseTimeToSecs(timespan2.duration)} seconds)`).dim()}`
), log$1(
`${format(`Files \u203A ${format(String(results2.passed + results2.failed)).bold()}`).dim()}`
), log$1(`
${message}
`);
}
}, createReporter = (events) => () => ({
...poku$1,
...events
}), classic = () => {
const files = {
passed: /* @__PURE__ */ new Map(),
failed: /* @__PURE__ */ new Map()
};
return createReporter({
onRunStart() {
hr(), log$1(`${format("Running Tests").bold()}
`);
},
onFileStart() {
},
onFileResult({ status, path, duration, output }) {
status ? files.passed.set(path.relative, duration) : files.failed.set(path.relative, duration), output && log$1(output);
},
onRunResult() {
if (hr(), files.passed.size > 0 && files.failed.size === 0) {
log$1(
Array.from(files.passed).map(
([file, time]) => `${indentation.test}${format("\u2714").success()} ${format(`${file} ${format(`\u203A ${time.toFixed(6)}ms`).success()}`).dim()}`
).join(`
`)
);
return;
}
files.failed.size > 0 && log$1(
Array.from(files.failed).map(
([file, time]) => `${indentation.test}${format("\u2718").fail()} ${format(`${file} ${format(`\u203A ${time.toFixed(6)}ms`).fail()}`).dim()}`
).join(`
`)
);
},
onExit(options) {
const { code } = options;
poku$1.onExit(options), log$1(
`${format("Exited with code").dim()} ${format(String(code)).bold()[code === 0 ? "success" : "fail"]()}
`
);
}
})();
}, LABEL_FILES_PASSED = "\x1B[2mtest file(s) passed\x1B[0m", LABEL_FILES_FAILED = "\x1B[2mtest file(s) failed\x1B[0m", summaryFooter = (countFails, { timespan: timespan2, results: results2 }) => {
countFails > 0 && hr(), log$1(`${format(String(results2.passed)).bold().dim()} ${LABEL_FILES_PASSED}`), log$1(`${format(String(results2.failed)).bold().dim()} ${LABEL_FILES_FAILED}`), log$1(
`${format(`Finished in \xB1${parseTimeToSecs(timespan2.duration)} seconds`).dim()}`
);
}, BADGE_PASS = "\x1B[102m\x1B[1m PASS \x1B[0m", BADGE_FAIL = "\x1B[101m\x1B[1m FAIL \x1B[0m", compact = () => {
let countFails = 0;
return createReporter({
onRunStart() {
},
onFileStart() {
},
onDescribeAsTitle() {
},
onSkipFile() {
},
onSkipModifier() {
},
onTodoModifier() {
},
onFileResult({ status, path, output }) {
log$1(`${status ? BADGE_PASS : BADGE_FAIL} ${path.relative}`), status || (countFails++, errors.push({
file: path.relative,
output
}));
},
onExit({ timespan: timespan2, results: results2 }) {
summaryFooter(countFails, { timespan: timespan2, results: results2 });
}
})();
}, DOT_PASS = ".", DOT_FAIL = "\x1B[1m\x1B[31mF\x1B[0m", dot = () => createReporter({
onRunStart() {
hr();
},
onDescribeAsTitle() {
},
onTodoModifier() {
},
onSkipModifier() {
},
onSkipFile() {
},
onFileResult({ path, status, output }) {
stdout.write(status ? DOT_PASS : DOT_FAIL), status || errors.push({
file: path.relative,
output
});
},
onRunResult(options) {
stdout.write(`
`), poku$1.onRunResult(options);
}
})(), focus = () => {
let countFails = 0;
return createReporter({
onRunStart() {
},
onDescribeAsTitle() {
},
onTodoModifier() {
},
onSkipModifier() {
},
onSkipFile() {
},
onFileResult({ status, output }) {
status || (countFails++, output && log$1(output));
},
onExit({ timespan: timespan2, results: results2 }) {
summaryFooter(countFails, { timespan: timespan2, results: results2 });
}
})();
}, reporter = {
poku: () => poku$1,
dot,
compact,
focus,
classic
};
var reporter$1 = /* @__PURE__ */ Object.freeze({ __proto__: null, reporter });
const states = getSharedState("states", {
isSinglePath: void 0
}), timespan = getSharedState("timespan", {
started: void 0,
finished: void 0,
duration: 0
}), errorHoist = getSharedState("errorHoist", {
depth: 0,
failed: !1
}), VERSION = "4.3.2", deepOptions = getSharedState("deepOptions", []), GLOBAL = getSharedState("GLOBAL", {
cwd: cwd$1,
configs: {
filter: void 0,
exclude: void 0,
concurrency: void 0,
timeout: void 0,
sequential: void 0,
quiet: void 0,
debug: void 0,
failFast: void 0,
isolation: void 0,
deno: void 0,
noExit: void 0,
reporter: void 0,
beforeEach: void 0,
afterEach: void 0,
testNamePattern: env.POKU_TEST_NAME_PATTERN ? new RegExp(escapeRegExp(env.POKU_TEST_NAME_PATTERN)) : void 0,
testSkipPattern: env.POKU_TEST_SKIP_PATTERN ? new RegExp(escapeRegExp(env.POKU_TEST_SKIP_PATTERN)) : void 0
},
configFile: void 0,
configsFromFile: /* @__PURE__ */ Object.create(null),
reporter: reporter[env.POKU_REPORTER && env.POKU_REPORTER in reporter ? env.POKU_REPORTER : "poku"](),
envFile: void 0,
runtime: env.POKU_RUNTIME || getRuntime(),
runAsOnly: !1
}), [, , ...processArgs] = argv, regexQuotes = /''|""/, getArg = (arg, prefix = "--", baseArgs = processArgs) => {
const argPattern = `${prefix}${arg}=`, argValue = baseArgs.find((a) => a.startsWith(argPattern));
if (argValue)
return argValue.slice(argPattern.length).replace(regexQuotes, "");
}, hasArg = (arg, prefix = "--", baseArgs = processArgs) => baseArgs.some((a) => a.startsWith(`${prefix}${arg}`)), getPaths = (prefix = "--", baseArgs = processArgs) => {
let hasPaths = !1;
const paths = [];
for (const arg of baseArgs)
arg.startsWith(prefix) || (hasPaths || (hasPaths = !0), paths.push(arg));
return hasPaths ? paths : void 0;
}, argToArray = (arg, prefix = "--", baseArgs = processArgs) => {
if (!hasArg(arg, prefix, baseArgs)) return;
const argValue = getArg(arg, prefix, baseArgs);
return argValue ? argValue.split(",").filter((a) => a) : [];
}, hasOnly = hasArg("only"), availableParallelism = () => typeof os.availableParallelism == "function" ? os.availableParallelism() : os.cpus()?.length ?? 0, isWindows = platform === "win32", runner = (filename) => {
const { configs, runtime } = GLOBAL;
if (runtime === "bun") return ["bun"];
if (runtime === "deno") {
const denoAllow = configs.deno?.allow ? configs.deno.allow.map((allow) => allow ? `--allow-${allow}` : "").filter((allow) => allow) : ["--allow-read", "--allow-env", "--allow-run", "--allow-net"], denoDeny = configs.deno?.deny ? configs.deno.deny.map((deny) => deny ? `--deny-${deny}` : "").filter((deny) => deny) : [];
return ["deno", "run", ...denoAllow, ...denoDeny];
}
const ext = extname(filename);
return ext === ".ts" || ext === ".mts" || ext === ".cts" ? ["node", "--import=tsx"] : ["node"];
}, SCRIPT_COMMANDS = {
npm: [isWindows ? "npm.cmd" : "npm", "run"],
bun: ["bun", "run"],
deno: ["deno", "task"],
yarn: ["yarn"],
pnpm: ["pnpm", "run"]
}, scriptRunner = (runner2) => [
...SCRIPT_COMMANDS[runner2] ?? SCRIPT_COMMANDS.npm
], eachCore = async (type, fileRelative) => {
const { configs } = GLOBAL;
if (typeof configs?.[type] != "function") return !0;
const cb = configs[type], showLogs = !configs.quiet, cbName = cb.name !== type ? cb.name : "anonymous function";
showLogs && log$1(
` ${format("\u25EF").dim().info()} ${format(`${type}: ${cbName}`).dim().italic()}`
);
try {
return await cb(), !0;
} catch (error) {
return showLogs && (log$1(
format(` \u2718 ${type} callback failed ${format(`\u203A ${cbName}`).dim()}`).fail().bold()
), log$1(format(` \u251C\u2500 Who's trying to run this ${type}?`).fail()), log$1(
format(` \u2502 \u2514\u2500 ${format(fileRelative).fail().underline()}`).fail()
), error instanceof Error && (log$1(format(" \u251C\u2500 Message:").fail()), log$1(format(` \u2502 \u2514\u2500 ${error.message}`).fail()))), !1;
}
}, beforeEach$1 = (fileRelative) => GLOBAL.configs.beforeEach ? eachCore("beforeEach", fileRelative) : !0, afterEach$1 = (fileRelative) => GLOBAL.configs.afterEach ? eachCore("afterEach", fileRelative) : !0, STDIO_IPC = ["inherit", "pipe", "pipe", "ipc"], STDIO_DEFAULT = ["inherit", "pipe", "pipe"], runTestFile = async (path) => {
const { configs } = GLOBAL;
if (configs.isolation === "none") {
const { runTestInProcess } = await import("./run-test-in-process.js");
return runTestInProcess(path);
}
const { cwd: cwd2, reporter: reporter2 } = GLOBAL;
let command = [...runner(path), path, ...deepOptions];
const plugins = configs.plugins;
if (plugins?.length) {
for (const plugin of plugins)
if (plugin.runner) {
command = plugin.runner(command, path);
break;
}
}
const runtime = command.shift(), file = relative(cwd2, path), showLogs = !configs.quiet, outputChunks = [];
let outputSize = 0;
const stdOut = (data) => {
outputSize < 10485760 && (outputChunks.push(data), outputSize += data.length);
}, start = hrtime();
let end;
return await beforeEach$1(file) ? (reporter2.onFileStart({
path: {
relative: file,
absolute: path
}
}), new Promise((resolve2) => {
const child = spawn(runtime, command, {
stdio: plugins?.some((plugin) => plugin.ipc) ? STDIO_IPC : STDIO_DEFAULT,
shell: !1
});
if (child.stdout.setEncoding("utf8"), child.stderr.setEncoding("utf8"), child.stdout.on("data", stdOut), child.stderr.on("data", stdOut), plugins?.length)
for (const plugin of plugins)
plugin.onTestProcess && plugin.onTestProcess(child, path);
let timedOut = !1, killTimer;
configs.timeout && (killTimer = setTimeout(() => {
timedOut = !0, outputChunks.push(timeoutMessage(configs.timeout)), setTimeout(() => child.kill("SIGTERM"), 250);
}, configs.timeout)), child.on("close", async (code) => {
killTimer && clearTimeout(killTimer), end = hrtime(start);
const result = timedOut ? !1 : code === 0;
if (showLogs) {
const output = outputChunks.join(""), parsedOutputs = parserOutput({
output,
result,
debug: GLOBAL.configs.debug
})?.join(`
`), total = end[0] * 1e3 + end[1] / 1e6;
reporter2.onFileResult({
status: result,
path: {
relative: file,
absolute: path
},
duration: total,
output: parsedOutputs
});
}
if (!await afterEach$1(file)) {
resolve2(!1);
return;
}
resolve2(result);
}), child.on("error", (err) => {
killTimer && clearTimeout(killTimer), end = hrtime(start);
const total = end[0] * 1e3 + end[1] / 1e6;
showLogs && console.error(`Failed to start test: ${path}`, err), reporter2.onFileResult({
status: !1,
path: {
relative: file,
absolute: path
},
duration: total
}), resolve2(!1);
});
})) : !1;
}, { cwd } = GLOBAL;
hasOnly && deepOptions.push("--only");
const runTests = (files) => {
let allPassed = !0, activeTests = 0, nextIndex = 0, resolveDone;
const { configs } = GLOBAL, showLogs = !configs.quiet, failFastError = ` ${format("\u2139").fail()} ${format("failFast").bold()} is enabled`, totalFiles = files.length, concurrency = (() => {
if (configs.sequential || configs.isolation === "none") return 1;
const limit = configs.concurrency ?? availableParallelism();
return limit <= 0 ? totalFiles || 1 : limit;
})(), done = new Promise((resolve2) => {
resolveDone = (passed) => {
resolve2(passed);
};
}), runNext = async () => {
if (nextIndex >= totalFiles && activeTests === 0) {
resolveDone(allPassed);
return;
}
if (nextIndex >= totalFiles) return;
const filePath = files[nextIndex++];
activeTests++, await runTestFile(filePath) ? ++results.passed : (++results.failed, allPassed = !1, configs.failFast && (showLogs && (hr(), console.error(failFastError), log$1(
`
${format("File:").bold()} ${format(`./${relative(cwd, filePath)}`).fail()}`
), hr()), exit$1(1))), activeTests--, runNext();
};
for (let i = 0; i < concurrency; i++) runNext();
return done;
}, exit = (code, quiet) => {
const isPoku = results.passed > 0 || results.failed > 0;
!quiet && isPoku && GLOBAL.reporter.onExit({
code,
timespan,
results
}), process$1.exitCode = code === 0 ? 0 : 1;
};
process$1.on("unhandledRejection", (err) => {
err instanceof AssertionError || console.error("unhandledRejection", err), process$1.exitCode = 1;
});
process$1.on("uncaughtException", (err) => {
err instanceof AssertionError || console.error("uncaughtException", err), process$1.exitCode = 1;
});
const regex$2 = {
sep: /[/\\]+/g,
pathLevel: /(\.\.(\/|\\|$))+/g,
unusualChars: /[<>|^?*]+/g,
absolutePath: /^[/\\]/,
defaultFilter: /\.(test|spec)\./i
}, sanitizePath = (input, ensureTarget) => {
const sanitizedPath = input.replace(regex$2.sep, sep).replace(regex$2.pathLevel, "").replace(regex$2.unusualChars, "");
return ensureTarget ? sanitizedPath.replace(regex$2.absolutePath, `.${sep}`) : sanitizedPath;
}, envFilter = env.FILTER?.trim() ? new RegExp(escapeRegExp(env.FILTER), "i") : void 0, getAllFilesInner = async (dirPath, files, filter, exclude) => {
try {
if ((await stat(dirPath)).isFile()) {
const fullPath = sanitizePath(dirPath);
if (fullPath.indexOf("node_modules") !== -1 || fullPath.indexOf(".git/") !== -1)
return files;
if (states?.isSinglePath)
return files.add(fullPath), files;
if (exclude) {
for (const pattern of exclude) if (pattern.test(fullPath)) return files;
}
return filter.test(fullPath) && files.add(fullPath), files;
}
const entries = await readdir(sanitizePath(dirPath), {
withFileTypes: !0
}), subdirs = [];
for (const entry of entries) {
if (entry.name === "node_modules" || entry.name === ".git") continue;
const fullPath = join(dirPath, entry.name);
exclude?.some((pattern) => pattern.test(fullPath)) || (entry.isFile() ? filter.test(fullPath) && files.add(fullPath) : entry.isDirectory() && subdirs.push(getAllFilesInner(fullPath, files, filter, exclude)));
}
return subdirs.length > 0 && await Promise.all(subdirs), files;
} catch (error) {
console.error(error), process.exit(1);
}
}, getAllFiles = (dirPath, files = /* @__PURE__ */ new Set(), configs) => {
const filter = envFilter ?? (configs?.filter instanceof RegExp ? configs.filter : regex$2.defaultFilter), exclude = configs?.exclude ? Array.isArray(configs.exclude) ? configs.exclude : [configs.exclude] : void 0;
return getAllFilesInner(dirPath, files, filter, exclude);
}, listFiles = async (targetDir, configs) => Array.from(await getAllFiles(sanitizePath(targetDir), /* @__PURE__ */ new Set(), configs));
var listFiles$1 = /* @__PURE__ */ Object.freeze({ __proto__: null, getAllFiles, listFiles, sanitizePath });
const onSigint = () => {
stdout.write("\x1B[?25h");
};
process.once("SIGINT", onSigint);
const poku = (async (targetPaths, configs) => {
configs && (GLOBAL.configs = { ...GLOBAL.configs, ...configs }), env.POKU_RUNTIME = GLOBAL.runtime, typeof GLOBAL.configs.reporter == "string" && (env.POKU_REPORTER = GLOBAL.configs.reporter), timespan.started = /* @__PURE__ */ new Date();
const start = hrtime(), paths = Array.prototype.concat(targetPaths), showLogs = !GLOBAL.configs.quiet, { reporter: plugin } = GLOBAL.configs, { cwd: cwd2 } = GLOBAL;
if (typeof plugin == "function")
GLOBAL.reporter = plugin(GLOBAL.configs);
else if (typeof plugin == "string" && plugin !== "poku") {
const { reporter: reporter2 } = await Promise.resolve().then(function() {
return reporter$1;
});
plugin in reporter2 && (GLOBAL.reporter = reporter2[plugin]());
}
const plugins = GLOBAL.configs.plugins;
let pluginContext, fileDiscoverer;
if (plugins?.length) {
pluginContext = {
configs: GLOBAL.configs,
runtime: GLOBAL.runtime,
cwd: GLOBAL.cwd,
configFile: GLOBAL.configFile,
runAsOnly: GLOBAL.runAsOnly,
results,
timespan,
reporter: GLOBAL.reporter
};
for (const plugin2 of plugins)
!fileDiscoverer && plugin2.discoverFiles && (fileDiscoverer = plugin2.discoverFiles), plugin2.setup && await plugin2.setup(pluginContext);
}
const testFiles = fileDiscoverer ? await fileDiscoverer(paths, pluginContext) : (await Promise.all(
paths.map((dir) => listFiles(join(cwd2, dir), GLOBAL.configs))
)).flat(1);
showLogs && GLOBAL.reporter.onRunStart();
const code = await runTests(testFiles) ? 0 : 1, end = hrtime(start), total = end[0] * 1e3 + end[1] / 1e6;
if (timespan.duration = total, timespan.finished = /* @__PURE__ */ new Date(), showLogs && GLOBAL.reporter.onRunResult({ code, timespan, results }), GLOBAL.configs.noExit || exit(code, GLOBAL.configs.quiet), plugins?.length && pluginContext)
for (const plugin2 of plugins)
plugin2.teardown && await plugin2.teardown(pluginContext);
if (GLOBAL.configs.noExit) return code;
}), assertProcessor = () => {
const { reporter: reporter2 } = GLOBAL, handleSuccess = ({ message }) => {
typeof message == "string" && reporter2.onAssertionSuccess({ message });
}, handleError = (error, options) => {
throw process$1.exitCode = 1, error instanceof AssertionError && reporter2.onAssertionFailure({ assertOptions: options, error }), error;
};
return { processAssert: (cb, options) => {
try {
cb(), handleSuccess(options);
} catch (error) {
handleError(error, options);
}
}, processAsyncAssert: async (cb, options) => {
try {
await cb(), handleSuccess(options);
} catch (error) {
handleError(error, options);
}
} };
}, { processAssert, processAsyncAssert } = assertProcessor(), isPredicate = (value) => typeof value == "function" || value instanceof RegExp || typeof value == "object", createAssert = (nodeAssert2) => {
const ok = (value, message) => processAssert(() => nodeAssert2.ok(value), { message });
return Object.assign(
((value, message) => ok(value, message)),
{
ok,
equal: (actual, expected, message) => {
processAssert(() => nodeAssert2.equal(actual, expected), { message });
},
deepEqual: (actual, expected, message) => processAssert(() => nodeAssert2.deepEqual(actual, expected), { message }),
strictEqual: (actual, expected, message) => processAssert(() => nodeAssert2.strictEqual(actual, expected), { message }),
deepStrictEqual: (actual, expected, message) => processAssert(() => nodeAssert2.deepStrictEqual(actual, expected), {
message
}),
doesNotMatch: (value, regExp, message) => {
processAssert(() => nodeAssert2.doesNotMatch(value, regExp), {
message,
actual: "Value",
expected: "RegExp",
defaultMessage: "Value should not match regExp"
});
},
doesNotReject: (async (block, errorOrMessage, message) => {
await processAsyncAssert(
async () => {
isPredicate(errorOrMessage) ? await nodeAssert2.doesNotReject(block, errorOrMessage, message) : await nodeAssert2.doesNotReject(block, message);
},
{
message: typeof errorOrMessage == "string" ? errorOrMessage : message,
defaultMessage: "Got unwanted rejection",
hideDiff: !0,
throw: !0
}
);
}),
throws: ((block, errorOrMessage, message) => {
isPredicate(errorOrMessage) ? processAssert(() => nodeAssert2.throws(block, errorOrMessage), {
message,
defaultMessage: "Expected function to throw",
hideDiff: !0
}) : processAssert(() => nodeAssert2.throws(block, message), {
message: typeof errorOrMessage < "u" ? errorOrMessage : message,
defaultMessage: "Expected function to throw",
hideDiff: !0
});
}),
doesNotThrow: ((block, errorOrMessage, message) => {
processAssert(
() => {
if (isPredicate(errorOrMessage))
nodeAssert2.doesNotThrow(block, errorOrMessage, message);
else {
const msg = typeof errorOrMessage == "string" ? errorOrMessage : message;
nodeAssert2.doesNotThrow(block, msg);
}
},
{
message: typeof errorOrMessage == "string" ? errorOrMessage : message,
defaultMessage: "Expected function not to throw",
hideDiff: !0,
throw: !0
}
);
}),
notEqual: (actual, expected, message) => processAssert(() => nodeAssert2.notEqual(actual, expected), {
message
}),
notDeepEqual: (actual, expected, message) => processAssert(() => nodeAssert2.notDeepEqual(actual, expected), { message }),
notStrictEqual: (actual, expected, message) => processAssert(() => nodeAssert2.notStrictEqual(actual, expected), {
message
}),
notDeepStrictEqual: (actual, expected, message) => processAssert(() => nodeAssert2.notDeepStrictEqual(actual, expected), {
message
}),
match: (value, regExp, message) => {
processAssert(() => nodeAssert2?.match(value, regExp), {
message,
actual: "Value",
expected: "RegExp",
defaultMessage: "Value should match regExp"
});
},
ifError: (value, message) => processAssert(() => nodeAssert2.ifError(value), {
message,
defaultMessage: "Expected no error, but received an error",
hideDiff: !0,
throw: !0
}),
fail: (message) => {
processAssert(() => nodeAssert2.fail(message), {
message,
defaultMessage: "Test failed intentionally",
hideDiff: !0
}), process.exit(1);
},
rejects: (async (block, errorOrMessage, message) => {
await processAsyncAssert(
async () => {
if (isPredicate(errorOrMessage))
await nodeAssert2.rejects(block, errorOrMessage, message);
else {
const msg = typeof errorOrMessage == "string" ? errorOrMessage : message;
await nodeAssert2.rejects(block, msg);
}
},
{
message: typeof errorOrMessage == "string" ? errorOrMessage : message,
defaultMessage: "Expected promise to be rejected with specified error",
hideDiff: !0,
throw: !0
}
);
})
}
);
}, assert = createAssert(nodeAssert), strict = createAssert(nodeAssert$1), each = {
before: {
status: !0,
cb: void 0
},
after: {
status: !0,
cb: void 0
}
}, getTitle = (input) => typeof input == "string" ? input : void 0, getCallback = (input) => typeof input == "function" ? input : void 0, onlyCall = /(?:^|[^.\w$])(?:it|test|describe)\.only\s*\(/, checkOnly = (cb) => typeof cb != "function" ? !1 : onlyCall.test(String(cb)), checkNoOnly = (cb) => typeof cb == "function" && !checkOnly(cb), todo = (async (messageOrCb, _cb) => {
const message = typeof messageOrCb == "string" ? messageOrCb : "Planning";
GLOBAL.reporter.onTodoModifier({ message });
}), skip$1 = (async (messageOrCb, _cb) => {
const message = typeof messageOrCb == "string" ? messageOrCb : "Skipping";
GLOBAL.reporter.onSkipModifier({ message });
}), createOnlyDescribe = (describeBase2) => (async (messageOrCb, cb) => {
if (hasOnly || (log$1(
format("Can't run `describe.only` tests without `--only` flag").fail()
), exit$1(1)), checkNoOnly(
typeof messageOrCb == "function" ? messageOrCb : cb
) && (GLOBAL.runAsOnly = !0), typeof messageOrCb == "string" && cb)
return describeBase2(messageOrCb, cb);
if (typeof messageOrCb == "function") return describeBase2(messageOrCb);
}), createOnlyIt = (itBase2) => (async (messageOrCb, cb) => {
if (hasOnly || (log$1(
format(
"Can't run `it.only` and `test.only` tests without `--only` flag"
).fail()
), exit$1(1)), typeof messageOrCb == "string" && cb) return itBase2(messageOrCb, cb);
if (typeof messageOrCb == "function") return itBase2(messageOrCb);
}), SCOPE_HOOKS_KEY = /* @__PURE__ */ Symbol.for("@pokujs/poku.test-scope-hooks"), getScopeHook = () => globalThis[SCOPE_HOOKS_KEY], itBase = async (titleOrCb, callback) => {
try {
const title = getTitle(titleOrCb), hasTitle = typeof title == "string", cb = getCallback(hasTitle ? callback : titleOrCb);
let success = !0, start, end;
if (GLOBAL.reporter.onItStart({ title }), hasTitle && indentation.itDepth++, typeof each.before.cb == "function") {
const beforeResult = each.before.cb();
beforeResult instanceof Promise && await beforeResult;
}
const insideDescribe = errorHoist.depth > 0;
let onError;
insideDescribe ? errorHoist.failed = !1 : (onError = (error) => {
process$1.exitCode = 1, success = !1, error instanceof AssertionError || console.error(error);
}, process$1.once("uncaughtException", onError), process$1.once("unhandledRejection", onError)), start = process$1.hrtime();
try {
const hooks = getScopeHook();
if (hooks) {
const holder = hooks.createHolder();
await hooks.runScoped(holder, (params) => cb(params));
} else {
const resultCb = cb();
resultCb instanceof Promise && await resultCb;
}
} catch (error) {
process$1.exitCode = 1, success = !1, error instanceof AssertionError || console.error(error);
} finally {
end = process$1.hrtime(start), onError ? (process$1.removeListener("uncaughtException", onError), process$1.removeListener("unhandledRejection", onError)) : errorHoist.failed && (success = !1, errorHoist.failed = !1);
}
if (typeof each.after.cb == "function") {
const afterResult = each.after.cb();
afterResult instanceof Promise && await afterResult;
}
if (!title) return;
const duration = end[0] * 1e3 + end[1] / 1e6;
indentation.itDepth--, GLOBAL.reporter.onItEnd({ title, duration, success });
} catch (error) {
if (indentation.itDepth > 0 && indentation.itDepth--, typeof each.after.cb == "function") {
const afterResult = each.after.cb();
afterResult instanceof Promise && await afterResult;
}
throw error;
}
}, itCore = (async (titleOrCb, cb) => {
if (!(GLOBAL.configs.testNamePattern && typeof titleOrCb == "string" && !GLOBAL.configs.testNamePattern.test(titleOrCb)) && !(GLOBAL.configs.testSkipPattern && typeof titleOrCb == "string" && GLOBAL.configs.testSkipPattern.test(titleOrCb)) && !(hasOnly && !GLOBAL.runAsOnly)) {
if (typeof titleOrCb == "string" && cb) return itBase(titleOrCb, cb);
if (typeof titleOrCb == "function") return itBase(titleOrCb);
}
}), it = Object.assign(itCore, {
todo,
skip: skip$1,
only: createOnlyIt(itBase)
}), test = it, getOptions = (input) => !input || typeof input != "object" ? void 0 : input, describeBase = async (titleOrCb, callbackOrOptions) => {
const { reporter: reporter2 } = GLOBAL, title = getTitle(titleOrCb), hasTitle = typeof title == "string", cb = getCallback(hasTitle ? callbackOrOptions : titleOrCb), hasCB = typeof cb == "function", options = hasCB ? void 0 : getOptions(callbackOrOptions);
let success = !0, start, end;
if (hasTitle && (hasCB ? (reporter2.onDescribeStart({ title }), indentation.describeDepth++) : reporter2.onDescribeAsTitle(title, options)), !hasCB) return;
const onError = (error) => {
process$1.exitCode = 1, success = !1, errorHoist.failed = !0, error instanceof AssertionError || console.error(error);
}, initialExitCode = process$1.exitCode;
errorHoist.depth++, process$1.once("uncaughtException", onError), process$1.once("unhandledRejection", onError), start = process$1.hrtime();
try {
const resultCb = cb();
resultCb instanceof Promise && await resultCb;
} catch (error) {
onError(error);
} finally {
end = process$1.hrtime(start), process$1.removeListener("uncaughtException", onError), process$1.removeListener("unhandledRejection", onError), errorHoist.depth--, process$1.exitCode !== initialExitCode && (success = !1);
}
if (!title) return;
const duration = end[0] * 1e3 + end[1] / 1e6;
indentation.describeDepth--, reporter2.onDescribeEnd({ title, duration, success }), GLOBAL.runAsOnly = !1;
}, describeCore = (async (messageOrCb, cbOrOptions) => {
if (typeof messageOrCb == "string" && typeof cbOrOptions != "function")
return describeBase(messageOrCb, cbOrOptions);
if (!(hasOnly && !checkOnly(
typeof messageOrCb == "function" ? messageOrCb : cbOrOptions
))) {
if (typeof messageOrCb == "string" && typeof cbOrOptions == "function")
return describeBase(messageOrCb, cbOrOptions);
if (typeof messageOrCb == "function") return describeBase(messageOrCb);
}
}), describe = Object.assign(describeCore, {
todo,
skip: skip$1,
only: createOnlyDescribe(describeBase)
}), removeComments = (input) => {
let inQuote = !1, quoteChar = "";
for (let i = 0; i < input.length; i++) {
const char = input[i];
if (inQuote)
char === quoteChar && input[i - 1] !== "\\" && (inQuote = !1);
else if (char === '"' || char === "'")
inQuote = !0, quoteChar = char;
else if (char === "#") return input.slice(0, i).trim();
}
return input.trim();
}, parseEnvLine = (line) => {
const index = line.indexOf("=");
if (index === -1) return null;
const arg = line.substring(0, index).trim(), value = line.substring(index + 1).trim().replace(/^['"]|['"]$/g, "");
return { arg, value };
}, resolveEnvVariables = (str, env2) => {
let result = "", rangeStart = 0, i = 0;
for (; i < str.length; )
if (str[i] === "$" && str[i + 1] === "{") {
result += str.slice(rangeStart, i), i += 2;
const varStart = i;
for (; i < str.length && str[i] !== "}"; ) i++;
result += env2[str.slice(varStart, i)] ?? "", i++, rangeStart = i;
} else
i++;
return rangeStart === 0 ? str : (rangeStart < str.length && (result += str.slice(rangeStart)), result);
}, regex$1 = {
comment: /^\s*#/
}, envFile = async (filePath = ".env") => {
const mapEnv = /* @__PURE__ */ new Map(), lines = (await readFile(sanitizePath(filePath), "utf8")).split(`
`).map((line) => removeComments(line.trim())).filter((line) => line.length > 0 && !regex$1.comment.test(line));
for (const line of lines) {
const parsedLine = parseEnvLine(line);
if (parsedLine) {
const { arg, value } = parsedLine;
mapEnv.set(arg, value && resolveEnvVariables(value, env));
}
}
for (const [arg, value] of mapEnv)
Object.prototype.hasOwnProperty.call(env, arg) || (env[arg] = value);
}, skip = (message = "Skipping") => {
message && GLOBAL.reporter.onSkipFile({ message }), exit$1(0);
}, beforeEach = (callback, options) => (options?.immediate && callback(), each.before.cb = () => {
if (each.before.status) return callback();
}, { pause: () => {
each.before.status = !1;
}, continue: () => {
each.before.status = !0;
}, reset: () => {
each.before.cb = void 0;
} }), afterEach = (callback) => (each.after.cb = () => {
if (each.after.status) return callback();
}, { pause: () => {
each.after.status = !1;
}, continue: () => {
each.after.status = !0;
}, reset: () => {
each.after.cb = void 0;
} }), regex = {
sequentialSpaces: /\s+/
}, setPortsAndPIDs = (portOrPID) => Array.isArray(portOrPID) ? portOrPID : [portOrPID].map((p) => Number(p)).filter((p) => !Number.isNaN(p)), populateRange = (startsAt, endsAt) => {
const first = Number(startsAt), last = Number(endsAt);
return Array.from({ length: last - first + 1 }, (_, i) => first + i);
}, killPID$1 = {
unix: (PID) => new Promise((resolve2) => {
spawn("kill", ["-9", String(Number(PID))], {
shell: !1
}).on("close", () => resolve2(void 0));
}),
windows: (PID) => new Promise((resolve2) => {
spawn(
"taskkill",
["/F", "/T", "/PID", String(Number(PID))],
{ shell: !1 }
).on("close", () => resolve2(void 0));
})
}, getPIDs$1 = {
unix: (port) => new Promise((resolve2) => {
const PIDs = /* @__PURE__ */ new Set(), service = spawn(
"lsof",
["-t", "-i", `:${Number(port)}`, "-s", "TCP:LISTEN"],
{ shell: !1 }
);
service.stdout.on("data", (data) => {
const output = data.toString().trim().split(`
`);
for (const pid of output)
pid && PIDs.add(Number(pid));
}), service.on("close", () => resolve2(Array.from(PIDs)));
}),
windows: (port) => new Promise((resolve2) => {
const PIDs = /* @__PURE__ */ new Set(), service = spawn("netstat", ["-aon"], { shell: !1 }), portMatch = `:${Number(port)}`;
service.stdout.on("data", (data) => {
const lines = data.toString().trim().trim().split(`
`);
for (const line of lines) {
if (!line.includes(portMatch)) continue;
const tokens = line.trim().split(regex.sequentialSpaces), stateIndex = tokens.indexOf("LISTENING");
if (stateIndex !== -1 && tokens[stateIndex + 1]) {
const pid = Number(tokens[stateIndex + 1]);
Number.isNaN(pid) || PIDs.add(pid);
}
}
}), service.on("close", () => resolve2(Array.from(PIDs)));
})
}, getPIDsByPorts = async (port) => {
const ports = setPortsAndPIDs(port), PIDs = [];
return await Promise.all(
ports.map(async (p) => {
PIDs.push(
...await (isWindows ? getPIDs$1.windows(p) : getPIDs$1.unix(p))
);
})
), PIDs;
}, getPIDsByRange = (startsAt, endsAt) => {
const ports = populateRange(startsAt, endsAt);
return getPIDs(ports);
}, getPIDs = Object.assign(getPIDsByPorts, {
range: getPIDsByRange
}), killPID = async (PID) => {
const PIDs = setPortsAndPIDs(PID);
await Promise.all(
PIDs.map(async (p) => {
isWindows ? await killPID$1.windows(p) : await killPID$1.unix(p);
})
);
}, killPort = async (port) => {
const PIDs = await getPIDs(port);
for (const PID of PIDs)
PID && await killPID(PID);
}, killRange = async (startsAt, endsAt) => {
const PIDs = await getPIDs.range(startsAt, endsAt);
for (const PID of PIDs)
PID && await killPID(PID);
}, kill = {
pid: killPID,
port: killPort,
range: killRange
}, runningProcesses = /* @__PURE__ */ new Map(), backgroundProcess = (runtime, args, file, options) => new Promise((resolve2, reject) => {
try {
let isResolved = !1;
const service = spawn(runtime, args, {
stdio: ["inherit", "pipe", "pipe"],
env: process$1.env,
timeout: options?.timeout,
cwd: options?.cwd ? sanitizePath(normalize(options.cwd)) : void 0,
shell: !1,
detached: !isWindows,
windowsHide: isWindows
}), PID = service.pid;
service.stdout.setEncoding("utf8"), service.stderr.setEncoding("utf8");
const end = (port) => new Promise((resolve22) => {
try {
if (runningProcesses.delete(PID), isWindows) {
kill.pid(PID);
return;
}
if (["bun", "deno"].includes(runtime) || ["bun", "deno"].includes(String(options?.runner)) ? process$1.kill(PID) : process$1.kill(-PID, "SIGKILL"), port)
setTimeout(async () => {
await kill.port(port), resolve22(void 0);
});
else {
resolve22(void 0);
return;
}
} catch {
resolve22(void 0);
return;
}
});
runningProcesses.set(PID, end);
const onData = (data) => {
!isResolved && typeof options?.startAfter != "number" && (typeof options?.startAfter > "u" || typeof options?.startAfter == "string" && String(data).includes(options.startAfter)) && (resolve2({ end }), clearTimeout(timeout), isResolved = !0), options?.verbose && log$1(data);
};
service.stdout.on("data", onData), service.stderr.on("data", onData), service.on("error", (err) => {
end(), reject(`Service failed to start: ${err}`);
}), service.on("close", (code) => {
code !== 0 && reject(`Service exited with code ${code}`);
});
const timeout = setTimeout(() => {
isResolved || (end(), reject(`createService: Timeout
File: ${file}`));
}, options?.timeout || 6e4);
typeof options?.startAfter == "number" && setTimeout(() => {
isResolved || (resolve2({ end }), clearTimeout(timeout), isResolved = !0);
}, options.startAfter);
} catch {
}
}), startService = (file, options) => {
const runtimeOptions = runner(file), runtime = runtimeOptions.shift(), runtimeArgs = [...runtimeOptions, file];
return backgroundProcess(
runtime,
runtimeArgs,
normalize(sanitizePath(file)),
options
);
}, startScript = (script, options) => {
const runner2 = options?.runner ?? "npm", runtimeOptions = scriptRunner(runner2), runtime = runtimeOptions.shift(), runtimeArgs = [...runtimeOptions, script];
return backgroundProcess(runtime, runtimeArgs, script, {
...options,
runner: runner2
});
};
process$1.once("SIGINT", async () => {
for (const end of runningProcesses.values()) await end();
});
const checkPort = (port, host) => new Promise((resolve2) => {
const client = createConnection(port, host);
client.on("connect", () => {
client.end(), resolve2(!0);
}), client.on("error", () => resolve2(!1));
}), sleep = (milliseconds) => {
if (!Number.isInteger(milliseconds))
throw new Error("Milliseconds must be an integer.");
return new Promise((resolve2) => setTimeout(resolve2, milliseconds));
}, waitForExpectedResult = async (callback, expectedResult, options) => {
const delay = options?.