From eacff9771733355955854f5f3af89dc084c0d302 Mon Sep 17 00:00:00 2001 From: Hexagon Date: Sun, 24 Mar 2024 23:43:59 +0100 Subject: [PATCH 01/13] Initial --- deno.json | 3 +- deps.ts | 2 - lib/cli/args.ts | 95 ++++++++++++--------------------------- lib/cli/main.ts | 101 ++++++++++++++++++++++++------------------ pup.ts | 2 +- test/cli/args.test.ts | 88 ++++++++---------------------------- 6 files changed, 109 insertions(+), 182 deletions(-) diff --git a/deno.json b/deno.json index d850d52..aec2005 100644 --- a/deno.json +++ b/deno.json @@ -17,5 +17,6 @@ "build-versions": "deno run --allow-read --allow-write --allow-env --unstable tools/release.ts && deno fmt", "build-webinterface": "cd plugins/web-interface && rm static/bundle.json; deno run --allow-read --allow-write https://deno.land/x/bundlee/bundlee.ts --bundle static static/bundle.json && deno fmt", "build": "deno task check && deno task build-schema && deno task build-webinterface && deno task build-versions" - } + }, + "imports": { "@cross/utils": "jsr:@cross/utils@^0.8.0" } } diff --git a/deps.ts b/deps.ts index dbf8097..931206e 100644 --- a/deps.ts +++ b/deps.ts @@ -13,8 +13,6 @@ * - Always use the same version of all imports from deno.land/std */ // cli -export { parse } from "https://deno.land/std@0.207.0/flags/mod.ts" -export type { Args } from "https://deno.land/std@0.207.0/flags/mod.ts" export * as path from "https://deno.land/std@0.207.0/path/mod.ts" export * as uuid from "https://deno.land/std@0.207.0/uuid/mod.ts" // logger diff --git a/lib/cli/args.ts b/lib/cli/args.ts index 2ef4d13..c44cb9a 100644 --- a/lib/cli/args.ts +++ b/lib/cli/args.ts @@ -5,7 +5,7 @@ * @license MIT */ -import { Args, parse } from "../../deps.ts" +import { ArgsParser } from "@cross/utils/args" /** * Parses command line arguments and returns a parsed object. @@ -13,63 +13,24 @@ import { Args, parse } from "../../deps.ts" * @param args - An array of command line arguments. * @returns - A parsed object containing the command line arguments. */ -function parseArguments(args: string[]): Args { - // All boolean arguments - const booleanArgs = [ - "setup", - "upgrade", - "help", - "autostart", - - "dry-run", - ] - - // All string arguments - const stringArgs = [ - "version", - "config", - "watch", - "cmd", - "worker", - "cwd", - "id", - - "cron", - "terminate", - - "instances", - "start-port", - "common-port", - "strategy", - "stdout", - "stderr", - - "unsafely-ignore-certificate-errors", - ] - - // All collection arguments - const collectArgs = [ - "env", - ] - - // And a list of aliases - const alias = { - "version": "v", - "help": "h", - "id": "I", - "autostart": "A", - "config": "c", - "cmd": "C", - "worker": "W", - "watch": "w", - "cron": "O", - "terminate": "T", - "cwd": "d", - "update": "upgrade", - "env": "e", +function parseArguments(args: string[]): ArgsParser { + const aliases = { + "v": "version", + "h": "help", + "I": "id", + "A": "autostart", + "c": "config", + "C": "cmd", + "W": "worker", + "w": "watch", + "O": "cron", + "T": "terminate", + "d": "cwd", + "upgrade": "update", + "e": "env", } - return parse(args, { alias, boolean: booleanArgs, string: stringArgs, collect: collectArgs, stopEarly: false, "--": true }) + return new ArgsParser(args, { aliases }) } /** @@ -78,7 +39,7 @@ function parseArguments(args: string[]): Args { * @returns - The parsed and checked arguments. * @throws - An error if any of the arguments are invalid. */ -function checkArguments(args: Args): Args { +function checkArguments(args: ArgsParser): ArgsParser { const validBaseArguments = [ "init", "append", @@ -126,28 +87,28 @@ function checkArguments(args: Args): Args { ] // Check that the base argument is either undefined or valid - const baseArgument = args._[0] + const baseArgument = args.getRest() if (baseArgument !== undefined && (typeof baseArgument !== "string" || !validBaseArguments.includes(baseArgument))) { throw new Error(`Invalid base argument: ${baseArgument}`) } - const hasDoubleDashCmd = args["--"] && args["--"].length > 0 - const hasCmd = hasDoubleDashCmd || args.cmd || args.worker + const hasDoubleDashCmd = args.hasRest() + const hasCmd = hasDoubleDashCmd || args.get("cmd") || args.get("worker") const expectConfigOptions = baseArgument === "init" || baseArgument === "append" || (baseArgument === "run" && hasCmd) const expectInstallerOptions = baseArgument === "setup" || baseArgument === "upgrade" || baseArgument === "update" // Only one type of command can be present at the same time - if ((hasDoubleDashCmd && args.cmd) || (args.cmd && args.worker) || (hasDoubleDashCmd && args.worker)) { + if ((hasDoubleDashCmd && args.get("cmd")) || (args.get("cmd") && args.get("worker")) || (hasDoubleDashCmd && args.get("worker"))) { throw new Error("'--cmd', '--worker' and '--' cannot be used at the same time.") } // Certain base arguments require --id - if (!args.id && (baseArgument === "init" || baseArgument === "append" || baseArgument === "remove")) { + if (!args.get("id") && (baseArgument === "init" || baseArgument === "append" || baseArgument === "remove")) { throw new Error("Arguments 'init', 'append', and 'remove' require '--id'") } // Init and append require a command - if ((args.init || args.append) && !hasCmd) { + if ((args.get("init") || args.get("append")) && !hasCmd) { throw new Error("Arguments 'init' and 'append' requires '--cmd' or '--worker'") } @@ -158,27 +119,27 @@ function checkArguments(args: Args): Args { // All arguments in processOptions require that init, append, cmd och worker is used for (const opt of processOptions) { - if (args[opt] && !expectConfigOptions) { + if (args.get(opt) && !expectConfigOptions) { throw new Error(`Argument '--${opt}' requires 'init', 'append', '--cmd' or '--worker'`) } } // All arguments in installerOptions require that setup or upgrade (or update) is used for (const opt of installerOptions) { - if (args[opt] && !expectInstallerOptions) { + if (args.get(opt) && !expectInstallerOptions) { throw new Error(`Argument '--${opt}' requires 'setup' or 'upgrade'`) } } // All arguments in numericArguments must be numeric for (const opt of numericArguments) { - if (args[opt] && isNaN(Number(args[opt]))) { + if (args.get(opt) && isNaN(Number(args.get(opt)))) { throw new Error(`Argument '--${opt}' must be a numeric value`) } } // --env flag can only be used with 'service install' base argument - if (args.env && (baseArgument !== "install")) { + if (args.get("env") && (baseArgument !== "install")) { throw new Error("Argument '--env' can only be used with 'service install' base argument") } diff --git a/lib/cli/main.ts b/lib/cli/main.ts index f2cf314..00a9e43 100644 --- a/lib/cli/main.ts +++ b/lib/cli/main.ts @@ -24,27 +24,36 @@ import { fileExists, toPersistentPath, toTempPath } from "../common/utils.ts" import { installService, jsonc, path, uninstallService } from "../../deps.ts" import { Logger } from "../core/logger.ts" +import { args } from "@cross/utils/args" + /** * Define the main entry point of the CLI application * * @private * @async */ -async function main(inputArgs: string[]) { - const args = parseArguments(inputArgs) +async function main() { + const parsedArgs = parseArguments(args()) // Extract base argument - const baseArgument = args._.length > 0 ? args._[0] : undefined - const secondaryBaseArgument = args._.length > 1 ? args._[1] : undefined + const baseArgument = parsedArgs.countLoose() ? parsedArgs.getLoose()[0] : undefined + const secondaryBaseArgument = parsedArgs.countLoose() > 1 ? parsedArgs.getLoose()[1] : undefined /** * setup, upgrade */ - const upgradeCondition = args.setup || baseArgument === "setup" - const setupCondition = args.upgrade || baseArgument === "upgrade" || baseArgument === "update" + const upgradeCondition = parsedArgs.get("setup") || baseArgument === "setup" + const setupCondition = parsedArgs.get("upgrade") || baseArgument === "upgrade" || baseArgument === "update" if (upgradeCondition || setupCondition) { try { - await upgrade(args.version, args.channel, args["unsafely-ignore-certificate-errors"], args["all-permissions"], args.local, setupCondition) + await upgrade( + parsedArgs.get("version"), + parsedArgs.get("channel"), + parsedArgs.get("unsafely-ignore-certificate-errors"), + parsedArgs.getBoolean("all-permissions"), + parsedArgs.getBoolean("local"), + setupCondition, + ) } catch (e) { console.error(`Could not ${setupCondition ? "install" : "upgrade"} pup, error: ${e.message}`) } @@ -55,7 +64,7 @@ async function main(inputArgs: string[]) { /** * version */ - if (args.version !== undefined || baseArgument === "version") { + if (parsedArgs.get("version") !== undefined || baseArgument === "version") { printHeader() Deno.exit(0) } @@ -63,10 +72,10 @@ async function main(inputArgs: string[]) { /** * help */ - if (args.help || !baseArgument || baseArgument === "help") { + if (parsedArgs.get("help") || !baseArgument || baseArgument === "help") { printUsage() console.log("") - printFlags(args["external-installer"]) + printFlags(parsedArgs.getBoolean("external-installer")) Deno.exit(0) } @@ -75,7 +84,7 @@ async function main(inputArgs: string[]) { */ let checkedArgs try { - checkedArgs = checkArguments(args) + checkedArgs = checkArguments(parsedArgs) } catch (e) { console.error(`Invalid combination of arguments: ${e.message}`) Deno.exit(1) @@ -84,15 +93,15 @@ async function main(inputArgs: string[]) { // Extract command from arguments let cmd if (checkedArgs) { - if (checkedArgs.cmd) { - cmd = checkedArgs.cmd - } else if (checkedArgs["--"] && checkedArgs["--"].length > 0) { - cmd = checkedArgs["--"].join(" ") + if (checkedArgs.get("cmd")) { + cmd = checkedArgs.get("cmd") + } else if (checkedArgs.hasRest()) { + cmd = checkedArgs.getRest() } } // Extract worker from arguments - const worker = checkedArgs.worker + const worker = checkedArgs.get("worker") /** * Now either @@ -104,7 +113,7 @@ async function main(inputArgs: string[]) { const useConfigFile = !runWithoutConfig let configFile if (useConfigFile) { - configFile = await findConfigFile(useConfigFile, checkedArgs.config, checkedArgs.cwd) + configFile = await findConfigFile(useConfigFile, checkedArgs.get("config"), checkedArgs.get("cwd")) } /** @@ -116,26 +125,26 @@ async function main(inputArgs: string[]) { Deno.exit(1) } - const system = args.system - const name = args.name || "pup" - const config = args.config - const cwd = args.cwd + const system = parsedArgs.getBoolean("system") + const name = parsedArgs.get("name") || "pup" + const config = parsedArgs.get("config") + const cwd = parsedArgs.get("cwd") const cmd = `pup run ${config ? `--config ${config}` : ""}` - const user = args.user - const home = args.home - const env = args.env || [] + const user = parsedArgs.get("user") + const home = parsedArgs.get("home") + const env = parsedArgs.getArray("env") || [] try { - await installService({ system, name, cmd, cwd, user, home, env }, args["dry-run"]) + await installService({ system, name, cmd, cwd, user, home, env }, parsedArgs.getBoolean("dry-run")) Deno.exit(0) } catch (e) { console.error(`Could not install service, error: ${e.message}`) Deno.exit(1) } } else if (baseArgument === "uninstall") { - const system = args.system - const name = args.name || "pup" - const home = args.home + const system = parsedArgs.getBoolean("system") + const name = parsedArgs.get("name") || "pup" + const home = parsedArgs.get("home") try { await uninstallService({ system, name, home }) @@ -157,7 +166,7 @@ async function main(inputArgs: string[]) { console.error(`Configuration file '${fallbackedConfigFile}' already exists, exiting.`) Deno.exit(1) } else { - await createConfigurationFile(fallbackedConfigFile, checkedArgs, cmd) + await createConfigurationFile(fallbackedConfigFile, checkedArgs, cmd!) console.log(`Configuration file '${fallbackedConfigFile}' created`) Deno.exit(0) } @@ -170,8 +179,8 @@ async function main(inputArgs: string[]) { */ if (baseArgument === "append") { if (configFile) { - await appendConfigurationFile(configFile, checkedArgs, cmd) - console.log(`Process '${args.id}' appended to configuration file '${configFile}'.`) + await appendConfigurationFile(configFile, checkedArgs, cmd!) + console.log(`Process '${parsedArgs.get("id")}' appended to configuration file '${configFile}'.`) Deno.exit(0) } else { console.log(`Configuration file '${configFile}' not found, use init if you want to create a new one. Exiting.`) @@ -182,7 +191,7 @@ async function main(inputArgs: string[]) { if (baseArgument === "remove") { if (configFile) { await removeFromConfigurationFile(configFile, checkedArgs) - console.log(`Process '${args.id}' removed from configuration file '${configFile}'.`) + console.log(`Process '${parsedArgs.get("id")}' removed from configuration file '${configFile}'.`) Deno.exit(0) } else { console.log("Configuration file '${fallbackedConfigFile}' not found, use init if you want to create a new one. Exiting.") @@ -221,16 +230,24 @@ async function main(inputArgs: string[]) { Deno.exit(1) } } else { - configuration = generateConfiguration(args.id || "task", cmd, args.cwd, args.cron, args.terminate, args.autostart, args.watch) + configuration = generateConfiguration( + parsedArgs.get("id") || "task", + cmd!, + parsedArgs.get("cwd"), + parsedArgs.get("cron"), + parsedArgs.get("terminate"), + parsedArgs.getBoolean("autostart"), + parsedArgs.get("watch"), + ) // Change working directory to configuration file directory - if (args.cwd) { + if (parsedArgs.get("cwd")) { // Change working directory of pup to whereever the configuration file is, change configFile to only contain file name try { - const resolvedPath = path.parse(path.resolve(args.cwd)) + const resolvedPath = path.parse(path.resolve(parsedArgs.get("cwd")!)) Deno.chdir(resolvedPath.dir) } catch (e) { - console.error(`Could not change working directory to path specified by --cwd ${args.cwd}, exiting. Message: `, e.message) + console.error(`Could not change working directory to path specified by --cwd ${parsedArgs.get("cwd")}, exiting. Message: `, e.message) Deno.exit(1) } } @@ -241,14 +258,14 @@ async function main(inputArgs: string[]) { if (baseArgument === "logs") { const logStore = `${await toPersistentPath(configFile as string)}/.main.log` const logger = new Logger(configuration.logger || {}, logStore) - const startTimestamp = args.start ? new Date(Date.parse(args.start)).getTime() : undefined - const endTimestamp = args.end ? new Date(Date.parse(args.end)).getTime() : undefined - const numberOfRows = args.n ? parseInt(args.n, 10) : undefined - let logs = await logger.getLogContents(args.id, startTimestamp, endTimestamp) + const startTimestamp = parsedArgs.get("start") ? new Date(Date.parse(parsedArgs.get("start")!)).getTime() : undefined + const endTimestamp = parsedArgs.get("end") ? new Date(Date.parse(parsedArgs.get("end")!)).getTime() : undefined + const numberOfRows = parsedArgs.get("n") ? parseInt(parsedArgs.get("n")!, 10) : undefined + let logs = await logger.getLogContents(parsedArgs.get("id"), startTimestamp, endTimestamp) logs = logs.filter((log) => { const { processId, severity } = log - const severityFilter = !args.severity || args.severity === "" || args.severity.toLowerCase() === severity.toLowerCase() - const processFilter = !args.id || args.id === "" || args.id.toLowerCase() === processId.toLowerCase() + const severityFilter = !parsedArgs.get("severity") || parsedArgs.get("severity") === "" || parsedArgs.get("severity")!.toLowerCase() === severity.toLowerCase() + const processFilter = !parsedArgs.get("id") || parsedArgs.get("id") === "" || parsedArgs.get("id")!.toLowerCase() === processId.toLowerCase() return severityFilter && processFilter }) if (numberOfRows) { diff --git a/pup.ts b/pup.ts index acb71bc..2f8bbc5 100644 --- a/pup.ts +++ b/pup.ts @@ -7,4 +7,4 @@ import { main } from "./lib/cli/main.ts" -main(Deno.args) +main() diff --git a/test/cli/args.test.ts b/test/cli/args.test.ts index 9416ed7..ed7073f 100644 --- a/test/cli/args.test.ts +++ b/test/cli/args.test.ts @@ -2,6 +2,8 @@ import { checkArguments, parseArguments } from "../../lib/cli/args.ts" import { assertEquals, assertThrows, spy } from "../deps.ts" import { Application } from "../../application.meta.ts" import { printHeader, printUsage } from "../../lib/cli/output.ts" +import { ArgsParser } from "jsr:@cross/utils@^0.8.0/args" +import { parseCatArgs } from "https://deno.land/x/dax@0.35.0/src/commands/cat.ts" Deno.test("Boolean options and aliases are parsed correctly", () => { const inputArgs = [ @@ -16,31 +18,15 @@ Deno.test("Boolean options and aliases are parsed correctly", () => { "--cmd", ] const parsedArgs = parseArguments(inputArgs) - const expectedArgs = { - /* Specified */ - version: "", - v: "", - - setup: false, - - help: true, - h: true, - "dry-run": false, - - autostart: true, - A: true, - - cmd: "", - C: "", - - update: false, - upgrade: false, - - _: ["init", "append", "status", "remove", "run"], - "--": [], - } - - assertEquals(parsedArgs, expectedArgs) + assertEquals(parsedArgs.getBoolean("version"), true); + assertEquals(parsedArgs.getBoolean("help"), true); + assertEquals(parsedArgs.getBoolean("autostart"), true); + assertEquals(parsedArgs.getLoose().includes("init"), true); + assertEquals(parsedArgs.getLoose().includes("append"), true); + assertEquals(parsedArgs.getLoose().includes("status"), true); + assertEquals(parsedArgs.getLoose().includes("remove"), true); + assertEquals(parsedArgs.getLoose().includes("run"), true); + assertEquals(parsedArgs.getLoose().includes("cmd"), true); }) Deno.test("String options and aliases are parsed correctly", () => { @@ -64,51 +50,15 @@ Deno.test("String options and aliases are parsed correctly", () => { "--dry-run", ] const parsedArgs = parseArguments(inputArgs) - const expectedArgs = { - /* Specified */ - config: "config.json", - c: "config.json", - - watch: "watched.ts", - w: "watched.ts", - - cmd: "command", - C: "command", - - worker: "worker_script", - W: "worker_script", - - cwd: "cwd", - d: "cwd", - - id: "id", - I: "id", - - cron: "cron", - O: "cron", - - terminate: "terminate", - T: "terminate", - - A: false, - autostart: false, - - /* All boolean options will be included in output too */ - help: false, - h: false, - "dry-run": true, - setup: false, - upgrade: false, - update: false, - - _: [], - "--": [], - } - assertEquals(parsedArgs, expectedArgs) + assertEquals(parsedArgs.get("config"), "config.json"); + assertEquals(parsedArgs.get("watch"), "watched.json"); + assertEquals(parsedArgs.get("cmd"), "command"); + assertEquals(parsedArgs.getBoolean("dry-run"), true); + }) Deno.test("checkArguments should throw error when autostart argument is provided without init, append or --cmd", async () => { - const args = { _: [], autostart: true } + const args = new ArgsParser(["--cron"]) await assertThrows( () => { checkArguments(args) @@ -163,7 +113,7 @@ Deno.test("checkArguments should throw error when cmd argument is provided witho }) Deno.test("checkArguments should throw error when worker argument is provided without init, append or run", async () => { - const args = { _: [], worker: "command" } + const args = new ArgsParser(["--worker", "command"]); await assertThrows( () => { checkArguments(args) @@ -174,7 +124,7 @@ Deno.test("checkArguments should throw error when worker argument is provided wi }) Deno.test("checkArguments should throw error when init or append argument is provided without cmd", async () => { - const args = { _: [], init: true } + const args = new ArgsParser(["init"]); await assertThrows( () => { checkArguments(args) From c393f62e29f4cddbf0c2a465ee8aeda94f9b7f16 Mon Sep 17 00:00:00 2001 From: Hexagon Date: Sun, 7 Apr 2024 21:53:32 +0200 Subject: [PATCH 02/13] First working build of cross-branch --- deno.json | 21 +++++++- deps.ts | 47 ----------------- lib/cli/config.ts | 64 ++++++++++++------------ lib/cli/main.ts | 7 ++- lib/cli/upgrade.ts | 10 ++-- lib/common/ipc.ts | 3 +- lib/common/utils.ts | 10 ++-- lib/core/configuration.ts | 2 +- lib/core/logger.ts | 4 +- lib/core/process.ts | 4 +- lib/core/pup.ts | 3 +- lib/core/runner.ts | 6 +-- lib/core/watcher.ts | 3 +- lib/core/worker.ts | 5 +- plugins/web-interface/static/bundle.json | 40 +++++++-------- test/cli/args.test.ts | 16 +++--- test/cli/columns.test.ts | 2 +- test/cli/configuration.test.ts | 2 +- test/cli/output.test.ts | 2 +- test/common/eventemitter.test.ts | 2 +- test/common/ipc.test.ts | 2 +- test/common/utils.test.ts | 2 +- test/core/loadbalancer.test.ts | 2 +- test/core/logger.test.ts | 2 +- test/core/plugin.test.ts | 2 +- test/core/pup.test.ts | 2 +- test/core/status.test.ts | 2 +- test/deps.ts | 2 - test/main.test.ts | 2 +- test/telemetry.test.ts | 4 +- tools/build-schema.ts | 2 +- tools/release.ts | 9 ++-- 32 files changed, 134 insertions(+), 152 deletions(-) delete mode 100644 deps.ts delete mode 100644 test/deps.ts diff --git a/deno.json b/deno.json index e21dabe..3830395 100644 --- a/deno.json +++ b/deno.json @@ -10,7 +10,7 @@ }, "tasks": { - "update-deps": "deno run --allow-read=. --allow-net=deno.land,cdn.deno.land https://deno.land/x/udd/main.ts --dry-run deno.json deps.ts test/deps.ts plugins/web-interface/deps.ts", + "update-deps": "deno run --allow-read=. --allow-net=jsr.io,registry.npmjs.org jsr:@check/deps", "check": "deno fmt --check && deno lint && deno check --unstable-kv pup.ts && deno test --allow-read --allow-write --allow-env --allow-net --allow-sys --allow-run --unstable-kv --coverage=cov_profile && echo \"Generating coverage\" && deno coverage cov_profile --exclude=pup/test --lcov --output=cov_profile.lcov", "check-coverage": "deno task check && genhtml cov_profile.lcov --output-directory cov_profile/html && lcov --list cov_profile.lcov && deno run --allow-net --allow-read https://deno.land/std/http/file_server.ts cov_profile/html", "build-schema": "deno run --allow-write --allow-read --allow-env=XDG_DATA_HOME,HOME tools/build-schema.ts && deno fmt", @@ -18,5 +18,22 @@ "build-webinterface": "cd plugins/web-interface && rm static/bundle.json; deno run --allow-read --allow-write https://deno.land/x/bundlee/bundlee.ts --bundle static static/bundle.json && deno fmt", "build": "deno task check && deno task build-schema && deno task build-webinterface && deno task build-versions" }, - "imports": { "@cross/utils": "jsr:@cross/utils@^0.8.0" } + + "imports": { + "@cross/fs": "jsr:@cross/fs@^0.0.8", + "@cross/service": "jsr:@cross/service@^1.0.0", + "@cross/utils": "jsr:@cross/utils@^0.9.4", + "@david/dax": "jsr:@david/dax@^0.40.0", + "@hexagon/croner": "jsr:@hexagon/croner@^8.0.1", + "@std/assert": "jsr:@std/assert@^0.221.0", + "@std/async": "jsr:@std/async@^0.221.0", + "@std/io": "jsr:@std/io@^0.221.0", + "@std/jsonc": "jsr:@std/jsonc@^0.221.0", + "@std/path": "jsr:@std/path@^0.221.0", + "@std/semver": "jsr:@std/semver@^0.221.0", + "@std/testing": "jsr:@std/testing@^0.221.0", + "@std/uuid": "jsr:@std/uuid@^0.221.0", + "zod": "npm:zod@^3.22.4", + "zod-to-json-schema": "npm:zod-to-json-schema@^3.22.5" + } } diff --git a/deps.ts b/deps.ts deleted file mode 100644 index 871c722..0000000 --- a/deps.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Re-exports the required methods and types from remote modules - * - * - Check for updates using `deno task update-deps` - * - Always pin all imports to a specific version - * - * @file deps.ts - */ - -/** - * Deno std dependencies - * - * - Always use the same version of all imports from deno.land/std - */ -// cli -export * as path from "https://deno.land/std@0.221.0/path/mod.ts" -export * as uuid from "https://deno.land/std@0.221.0/uuid/mod.ts" -// logger -export { stripColor } from "https://deno.land/std@0.221.0/fmt/colors.ts" -// config -export * as jsonc from "https://deno.land/std@0.221.0/jsonc/mod.ts" -// watcher -export { delay } from "https://deno.land/std@0.221.0/async/mod.ts" -export { globToRegExp, relative } from "https://deno.land/std@0.221.0/path/mod.ts" -// core - process -export { StringReader } from "https://deno.land/std@0.221.0/io/string_reader.ts" -export { readLines } from "https://deno.land/std@0.221.0/io/mod.ts" -// service installer, release tool -export { existsSync } from "https://deno.land/std@0.221.0/fs/mod.ts" -// ipc -export { debounce } from "https://deno.land/std@0.221.0/async/mod.ts" -export { basename, dirname, join, resolve } from "https://deno.land/std@0.221.0/path/mod.ts" -// upgrader -export { greaterThan, lessThan, parse as parseVersion } from "https://deno.land/std@0.221.0/semver/mod.ts" -export type { SemVer } from "https://deno.land/std@0.221.0/semver/mod.ts" - -/** - * Third party dependencies - * - * - Prefer deno.land/x when available - */ -export { Cron } from "https://deno.land/x/croner@8.0.1/dist/croner.js" -export { z } from "https://deno.land/x/zod@v3.22.4/mod.ts" -export { installService, uninstallService } from "https://deno.land/x/service@1.0.0-rc.0/mod.ts" -export type { InstallServiceOptions, UninstallServiceOptions } from "https://deno.land/x/service@1.0.0-rc.0/mod.ts" -export { $ } from "https://deno.land/x/dax@0.39.2/mod.ts" -export { CommandChild } from "https://deno.land/x/dax@0.39.2/src/command.ts" diff --git a/lib/cli/config.ts b/lib/cli/config.ts index f863990..3803f05 100644 --- a/lib/cli/config.ts +++ b/lib/cli/config.ts @@ -7,8 +7,10 @@ */ import { Configuration, generateConfiguration, ProcessConfiguration } from "../core/configuration.ts" -import { Args, join, jsonc, resolve } from "../../deps.ts" +import * as jsonc from "@std/jsonc" +import { join, resolve } from "@std/path" import { fileExists } from "../common/utils.ts" +import { ArgsParser } from "@cross/utils" /** * Helper which creates a configuration file from command line arguments @@ -16,22 +18,22 @@ import { fileExists } from "../common/utils.ts" * @private * @async */ -export async function createConfigurationFile(configFile: string, checkedArgs: Args, cmd: string) { +export async function createConfigurationFile(configFile: string, checkedArgs: ArgsParser, cmd: string) { try { const config = generateConfiguration( - checkedArgs.id, + checkedArgs.get("id")!, cmd, - checkedArgs.cwd, - checkedArgs.cron, - checkedArgs.terminate, - checkedArgs.autostart, - checkedArgs.watch, - checkedArgs.instances, - checkedArgs["start-port"], - checkedArgs["common-port"], - checkedArgs.strategy, - checkedArgs.stdout, - checkedArgs.stderr, + checkedArgs.get("cwd"), + checkedArgs.get("cron"), + checkedArgs.get("terminate"), + checkedArgs.getBoolean("autostart"), + checkedArgs.get("watch"), + checkedArgs.get("instances"), + checkedArgs.get("start-port"), + checkedArgs.get("common-port"), + checkedArgs.get("strategy"), + checkedArgs.get("stdout"), + checkedArgs.get("stderr"), ) await Deno.writeTextFile(configFile, JSON.stringify(config, null, 2)) } catch (e) { @@ -46,7 +48,7 @@ export async function createConfigurationFile(configFile: string, checkedArgs: A * @private * @async */ -export async function appendConfigurationFile(configFile: string, checkedArgs: Args, cmd: string) { +export async function appendConfigurationFile(configFile: string, checkedArgs: ArgsParser, cmd: string) { try { // Read existing configuration let existingConfigurationObject @@ -64,19 +66,19 @@ export async function appendConfigurationFile(configFile: string, checkedArgs: A // Generate new configuration const newConfiguration = generateConfiguration( - checkedArgs.id, + checkedArgs.get("id")!, cmd, - checkedArgs.cwd, - checkedArgs.cron, - checkedArgs.terminate, - checkedArgs.autostart, - checkedArgs.watch, - checkedArgs.instances, - checkedArgs["start-port"], - checkedArgs["common-port"], - checkedArgs.strategy, - checkedArgs.stdout, - checkedArgs.stderr, + checkedArgs.get("cwd"), + checkedArgs.get("cron"), + checkedArgs.get("terminate"), + checkedArgs.getBoolean("autostart"), + checkedArgs.get("watch"), + checkedArgs.get("instances"), + checkedArgs.get("start-port"), + checkedArgs.get("common-port"), + checkedArgs.get("strategy"), + checkedArgs.get("stdout"), + checkedArgs.get("stderr"), ) const newProcess = newConfiguration.processes[0] @@ -100,7 +102,7 @@ export async function appendConfigurationFile(configFile: string, checkedArgs: A * * @async */ -export async function removeFromConfigurationFile(configFile: string, checkedArgs: Args) { +export async function removeFromConfigurationFile(configFile: string, checkedArgs: ArgsParser) { try { // Read existing configuration let existingConfigurationObject @@ -116,13 +118,13 @@ export async function removeFromConfigurationFile(configFile: string, checkedArg } // Remove from configuration - const alreadyExists = existingConfigurationObject.processes?.find((p: ProcessConfiguration) => p?.id === checkedArgs?.id) + const alreadyExists = existingConfigurationObject.processes?.find((p: ProcessConfiguration) => p?.id === checkedArgs.get("id")) if (!alreadyExists) { - throw new Error(`Process id '${checkedArgs?.id}' not found, exiting.`) + throw new Error(`Process id '${checkedArgs.get("id")}' not found, exiting.`) } // Filter out - existingConfigurationObject.processes = existingConfigurationObject.processes.filter((p: ProcessConfiguration) => p?.id !== checkedArgs?.id) + existingConfigurationObject.processes = existingConfigurationObject.processes.filter((p: ProcessConfiguration) => p?.id !== checkedArgs.get("id")) // Append new process, and write configuration file await Deno.writeTextFile(configFile, JSON.stringify(existingConfigurationObject, null, 2)) diff --git a/lib/cli/main.ts b/lib/cli/main.ts index 00a9e43..98d0ed3 100644 --- a/lib/cli/main.ts +++ b/lib/cli/main.ts @@ -21,11 +21,14 @@ import { upgrade } from "./upgrade.ts" import { fileExists, toPersistentPath, toTempPath } from "../common/utils.ts" // Import external dependencies -import { installService, jsonc, path, uninstallService } from "../../deps.ts" +import * as jsonc from "@std/jsonc" +import * as path from "@std/path" import { Logger } from "../core/logger.ts" import { args } from "@cross/utils/args" +import { installService, uninstallService } from "@cross/service" + /** * Define the main entry point of the CLI application * @@ -52,7 +55,7 @@ async function main() { parsedArgs.get("unsafely-ignore-certificate-errors"), parsedArgs.getBoolean("all-permissions"), parsedArgs.getBoolean("local"), - setupCondition, + setupCondition as boolean, ) } catch (e) { console.error(`Could not ${setupCondition ? "install" : "upgrade"} pup, error: ${e.message}`) diff --git a/lib/cli/upgrade.ts b/lib/cli/upgrade.ts index 770a953..b511e9f 100644 --- a/lib/cli/upgrade.ts +++ b/lib/cli/upgrade.ts @@ -9,7 +9,7 @@ */ import { Application } from "../../application.meta.ts" -import { greaterThan, lessThan, parseVersion } from "../../deps.ts" +import { greaterThan, lessThan, parse } from "@std/semver" const VERSION_INVENTORY_URL = "https://deno.land/x/pup/versions.json" const LOCAL_VERSION_INVENTORY_FILE = "./versions.json" @@ -44,8 +44,8 @@ async function getVersions(local = false): Promise { // Determine if the current Deno version meets the required version function denoVersionCheck(requiredVersion: string | null): boolean { if (requiredVersion === null) return false - const denoVersion = parseVersion(Deno.version.deno) - const required = parseVersion(requiredVersion) + const denoVersion = parse(Deno.version.deno) + const required = parse(requiredVersion) if (denoVersion !== null && required !== null && !lessThan(denoVersion, required)) { return true } else { @@ -65,7 +65,7 @@ export async function upgrade( // Determine the channel from the version if it's not specified if (version && !channelName) { - const semver = parseVersion(version) + const semver = parse(version) channelName = semver && semver.prerelease && semver.prerelease.length > 0 ? "prerelease" : "stable" } @@ -140,7 +140,7 @@ export async function upgrade( // Determine version to install const upgradeOrDowngradingAction = freshInstall ? "Installing" - : (canaryInstall ? "Upgrading" : greaterThan(parseVersion(Application.version), parseVersion((requestedVersion as Version).version)) ? "Downgrading" : "Upgrading") + : (canaryInstall ? "Upgrading" : greaterThan(parse(Application.version), parse((requestedVersion as Version).version)) ? "Downgrading" : "Upgrading") // If upgrading to a version that requires --unstable, alert the user if (unstableInstall) { diff --git a/lib/common/ipc.ts b/lib/common/ipc.ts index 4e83f55..0556e68 100644 --- a/lib/common/ipc.ts +++ b/lib/common/ipc.ts @@ -10,7 +10,8 @@ */ import { fileExists } from "../common/utils.ts" -import { basename, debounce, dirname, join, resolve } from "../../deps.ts" +import { basename, dirname, join, resolve } from "@std/path" +import { debounce } from "@std/async" export interface IpcValidatedMessage { pid: number | null diff --git a/lib/common/utils.ts b/lib/common/utils.ts index 3adf9eb..67d1ea7 100644 --- a/lib/common/utils.ts +++ b/lib/common/utils.ts @@ -5,7 +5,7 @@ * @license MIT */ -import { path } from "../../deps.ts" +import { parse, resolve } from "@std/path" /** * Check if a file exists. @@ -64,8 +64,8 @@ export async function dirExists(dirPath: string): Promise { * @returns {string} The temporary path associated with the configuration file. */ export async function toTempPath(configFile: string) { - const resolvedPath = path.parse(path.resolve(configFile)) - const tempPath = path.resolve(`${resolvedPath.dir}/.${resolvedPath.name}${resolvedPath.ext}-tmp`) + const resolvedPath = parse(resolve(configFile)) + const tempPath = resolve(`${resolvedPath.dir}/.${resolvedPath.name}${resolvedPath.ext}-tmp`) await Deno.mkdir(tempPath, { recursive: true }) return tempPath } @@ -77,8 +77,8 @@ export async function toTempPath(configFile: string) { * @returns {string} The persistent storage path associated with the configuration file. */ export async function toPersistentPath(configFile: string) { - const resolvedPath = path.parse(path.resolve(configFile)) - const persistentStoragePath = path.resolve(`${resolvedPath.dir}/.${resolvedPath.name}${resolvedPath.ext}-data`) + const resolvedPath = parse(resolve(configFile)) + const persistentStoragePath = resolve(`${resolvedPath.dir}/.${resolvedPath.name}${resolvedPath.ext}-data`) await Deno.mkdir(persistentStoragePath, { recursive: true }) return persistentStoragePath } diff --git a/lib/core/configuration.ts b/lib/core/configuration.ts index a68656a..465fe7c 100644 --- a/lib/core/configuration.ts +++ b/lib/core/configuration.ts @@ -5,7 +5,7 @@ * @license MIT */ -import { z } from "../../deps.ts" +import { z } from "zod" export const DEFAULT_INTERNAL_LOG_HOURS = 48 export const MAINTENANCE_INTERVAL_MS = 900_000 diff --git a/lib/core/logger.ts b/lib/core/logger.ts index b3e9ede..117503c 100644 --- a/lib/core/logger.ts +++ b/lib/core/logger.ts @@ -6,7 +6,7 @@ * @license MIT */ -import { stripColor } from "../../deps.ts" +import { stripAnsi } from "@cross/utils" import { GlobalLoggerConfiguration, KV_SIZE_LIMIT_BYTES, ProcessConfiguration } from "./configuration.ts" export interface LogEvent { @@ -197,7 +197,7 @@ class Logger { private async writeFile(fileName: string, text: string, quiet = false) { // Strip colors - text = stripColor(text) + text = stripAnsi(text) try { await Deno.writeTextFile(fileName, `${text}\n`, { append: true }) } catch (_e) { diff --git a/lib/core/process.ts b/lib/core/process.ts index 13c8bc6..7353b40 100644 --- a/lib/core/process.ts +++ b/lib/core/process.ts @@ -6,13 +6,15 @@ */ import { Pup } from "./pup.ts" -import { Cron, delay } from "../../deps.ts" import { Runner } from "./runner.ts" import { WorkerRunner } from "./worker.ts" import { ProcessConfiguration } from "./configuration.ts" import { Watcher } from "./watcher.ts" import { TelemetryData } from "../../telemetry.ts" +import { Cron } from "@hexagon/croner" +import { delay } from "@std/async" + /** * Represents the state of a process in Pup. * diff --git a/lib/core/pup.ts b/lib/core/pup.ts index 7fc6d5e..2f95b50 100644 --- a/lib/core/pup.ts +++ b/lib/core/pup.ts @@ -12,9 +12,10 @@ import { Process, ProcessState } from "./process.ts" import { Status } from "./status.ts" import { Plugin } from "./plugin.ts" import { Cluster } from "./cluster.ts" -import { path, uuid } from "../../deps.ts" +import * as path from "@std/path" import { EventEmitter } from "../common/eventemitter.ts" import { toPersistentPath, toTempPath } from "../common/utils.ts" +import * as uuid from "@std/uuid" interface InstructionResponse { success: boolean diff --git a/lib/core/runner.ts b/lib/core/runner.ts index 35526e6..4a8674c 100644 --- a/lib/core/runner.ts +++ b/lib/core/runner.ts @@ -6,9 +6,9 @@ */ import { ProcessConfiguration, Pup } from "./pup.ts" -import { $, CommandChild, readLines, StringReader } from "../../deps.ts" +import { readLines, StringReader } from "@std/io" import { BaseRunner, RunnerCallback, RunnerResult } from "../types/runner.ts" - +import { $, CommandChild } from "@david/dax" /** * Represents a task runner that executes tasks as regular processes. * Extends the BaseRunner class. @@ -130,7 +130,7 @@ class Runner extends BaseRunner { * @returns The command to be executed. */ private prepareCommand(env: Record) { - let child = $.raw`${this.processConfig.cmd}`.stdout("piped").stderr("piped") + let child = $.raw`${this.processConfig.cmd!}`.stdout("piped").stderr("piped") if (this.processConfig.cwd) child = child.cwd(this.processConfig.cwd) if (env) child = child.env(env) diff --git a/lib/core/watcher.ts b/lib/core/watcher.ts index e2a6c32..42aa049 100644 --- a/lib/core/watcher.ts +++ b/lib/core/watcher.ts @@ -6,7 +6,8 @@ * @license MIT */ -import { delay, globToRegExp, relative } from "../../deps.ts" +import { globToRegExp, relative } from "@std/path" +import { delay } from "@std/async" type FileAction = "any" | "access" | "create" | "modify" | "remove" | "other" diff --git a/lib/core/worker.ts b/lib/core/worker.ts index 8d87012..6f6a0dd 100644 --- a/lib/core/worker.ts +++ b/lib/core/worker.ts @@ -6,7 +6,8 @@ */ import { ProcessConfiguration, Pup } from "./pup.ts" -import { path, readLines, StringReader } from "../../deps.ts" +import { readLines, StringReader } from "@std/io" +import { resolve } from "@std/path" import { BaseRunner, RunnerCallback, RunnerResult } from "../types/runner.ts" class WorkerRunner extends BaseRunner { @@ -55,7 +56,7 @@ class WorkerRunner extends BaseRunner { if (this.pup.persistentStoragePath) env.PUP_DATA_STORAGE = this.pup.persistentStoragePath const workingDir = this.processConfig.cwd || Deno.cwd() - const workingDirUrl = new URL(`file://${path.resolve(workingDir)}/`).href + const workingDirUrl = new URL(`file://${resolve(workingDir)}/`).href try { this.worker = new Worker( diff --git a/plugins/web-interface/static/bundle.json b/plugins/web-interface/static/bundle.json index 4c61aa1..f661e80 100644 --- a/plugins/web-interface/static/bundle.json +++ b/plugins/web-interface/static/bundle.json @@ -1,37 +1,37 @@ { - "static/css/web-interface.css": { - "content": "H4sIAAAAAAAA_5RYa2_buBL97l_Bm-ICbWA5svyIqwAX2Hbv7r3A7n7p9gdQEmVzI5ECSTfJBv3vCz7Elyi7SdA64pwZcQ7njEa-uwULAD7TvqcEQNIAcUI9At8gw7DqEAeL27tFySgV4HWxACDLWkpE1sIedy8lyOAwdCjjL1ygfvmpw-Txd1h_UZe_UCKWN1_QkSLw9f83y5s_qKDgCyT8Zvk_1H1DAtdw-RPDsFtySHjGEcPt8uYnGRJ8ph1l4L89_QvfuChm4cHthOO_UQny1f2Oof5BbvHuFvwM2eMkk9s75VUdswayR8TURwne5Zt8m28ffONoWhfrj-vamnrU4HNvjXVRbzbW2OHjSYy2ot42-9zajpQ21g1tt0VlTRW0lu1uvV0jaxkY7iF7cX5Fs3XbJOgsGOzsDYvisFlb6xNkBJOjjbzdFmPkNs6_hW2LisA4Ywrzr2q0bXfWGOTfyh93Qz__fd4cDtCavPxrtKl3H60lyn9XHQ61OYt2kv_usN_dO98o_6ZqdrtGW2tV61lFWYNYVssyMzB2rN4fiiX4uF6CdZ5_GKvpN5nZ9XJSBMjcC_kb1tNoQ7n8jQtqtNZ541s1o6MRFnXRuLCK0tHWfGzvGxjUlN0NbHbNblJUNuqhzceD8qrKBlY_k7KywVu0rV0JhERIOjeHJdjul2C3_RAW2BVMSMwcKiBIgvaHJbjfL8Fh70ABUb74TPGNJl99XvU5Tyc_r_xGs68_r_5scE-AqQL0clgXhyVYb_ZLsN7KXL8vFne3CwAA-FNVIEcdqgWmRHbmijYvK0koeNW1-pVjcgRq5VrBltL2Pm6IhjizGGEihD4lD-M1CIcyuVmQaxQOIw_Jg9h-4RAV9AFj13B2c14exu8fDmcOzsP5ncThzAl6OL-nhNXsuAyba4iKMBEi5DJstlHFe6AJl6bgPUjMpS57DxBx6WrfwyS4dCLwcAkunRo8XILLhCxGh7mWrdShJKB4CDWgl94uAuV3WQURZE4GESytgwiUEkIEmSohAsxLIQLOayECzovBB15QQwIWyyGCzOkhgqUFEYFSioggU0lEgHlNRMB5UUTAeVX4wDfKYnRVTw052v9M63OPiABPuEHgDnymREBMEOPgDvyKCGK4BqhDEsPlI2V1wk2DCHgFoMF86OBLCQglCPwL9wNlAhLxAL4vFisuGCVHiVOD-BPSR1HRrtEAe_zgFQQbt4YPEjjiZlAWowsggdIGizO9YAIz1BiULIIESC5bTAVTkAo6hKmBBMpYLNIUQQJpLBZpqiCBNBaFXKxsywKvoIL145HRM2nCErEYHd1cXnewcEv6BQef_7GvXfbwjsI0uct4dyq6411G2wNyne-yQ3BWrgtedgqOzXXEy07hCZ6HQZ2eQM8iEwwS3lLWl2q5hhw9hMoSJ0yUIxx9GlRTBuUwWEqJSiMsT_Sbi5pAqCemelYGr_KmytySakDeO7YHkAvK3GGCspPZ33qlXgV6yI6YlEC9yFhbnn87yQXbU9oOPas-1VIq1IbB1Axk2xIn5f9veTlQjlU2oMXPSL2ZVFQI2pcgH4J4_wFcz8k61Q49l0AN6ANsGtV389VOfmUQJGm_SEhs5a8zF7h9yWpKBCKiBDUiAjFpgh0-kgwL1HO3PE7uX2TnHSBBsr1CLq9eU_HlZ9ZgprddSvmfe6IoxSTTRGxylaZk-dksbccl5X9k9MkkKuug7ehT9lICeBZU7kjfPuTGEJJ1qBXl2mSvTzEz5OarQtMyRjgVytWgBB0c5ILvamC0RpxnNWSNf29JvHUP9gOiDTFdT_m4apkBlpqrxDL6NHto8e5Bvhp38ANNdvF98c7PUdWKetxOmc4njGT63Q5pan6kYU04xf6xaK7y1SZiX2DR6RqcPrknEe0VF1Cc-bzb3S34HWKiKh28_5PSroJMfafY0SP_IEfvxTv556wgNdHfR9jA9CZtQ1kVxt7L-0RR3iCnUCiyEwuz29fZ8hsb2SbZHeTrua2oRH9Qd3xicCiB_D-86UrAanpsYD3Tht50k3dQZc-jQrze21QwRJoLQpnQqLrdJ9gcEZezK_jKEQf2CS8rwU5e8sIMruq6pUyPO5gc1RRayTBaBxo2Pgi0xs0iFy8dKgGnHVY7tXlhoh5NVUfrx7Dpo171GvWR7-yVF5TBBp-5Uv7YkoInxD7sSFrMqxFqtMqOFXxf7HbL8V--uve-zfmkbqUHbn3bN-Yqhw69Lu_vO8tr2xODCMoyCYNJg2soKMt-IOD6BwIao6CDH0XQIR1EGq5vaj5acT2asemW7ofRK-l9GdtcMDd7J17N4rnbuVx2COBu9k47BHO3OZBx9k57-HO3J740PngbGjmBF9Bu7tZ2b_ZOO4Rzt8Z4s3faKZy7jV7c7J128udurz6j9OXKVQokKKAh5RVTITETOlKOKUokbkJLypkkqFFCielJOQevJne34NNZCErUN2aV-tPTjf5GINky4ylOjdkM9fZBWp8ZlzceKLYPEtddR1RiwpiMNeNY-Q8AAAD__wMA9KeNLsQcAAA", - "contentType": "text/css; charset=UTF-8", - "lastModified": 1712444256451 - }, - "static/web-interface.html": { - "content": "H4sIAAAAAAAA_8yZXW_bNhSG7_MrWO5mQycz2ReGQRLgOW5aLKudyG6x3VHikcyEIlWSsuP--kFfsWzHrpQ6Q69kUnzPeXhIioe0--pyMpr9Mx2jhU2Ff-YWDySoTDwMEvtnCLkLoKz4gZCbgqUoWlBtwHp4Pnvj_I7bryRNwcNLDqtMaYtRpKQFaT284swuPAZLHoFTFn5EXHLLqXBMRAV4F4PzxpTlVoA_zTOXVD-rasHlPdIgPGzsWoBZAFiMFhpiD0fGkBWEDpcWdEwjGETG4A7ChbWZ-YOQiMk7M4iEylksqIZBpFJC7-gDETw0JFbSOnQFRqVAfhv8PDgnhUsqxCDlsnSGCt-J5nbtYbOgv1785ASfhX74OBdZnH--eZ1F85EyD1Eibm7fDz-Ry3_vPpjRuRj_cv5ghpcmhhs1Xmfj1yBX0YRf3d2TMMivrv56O17fBerm4vMHbqi88TyMIq2MUZonXHqYSiXXqcoNRhpi0Bp0pgSP1h6WymmqMCLlcJJmPN1QsTWKBDXGw4zqexQmTvmMqyf2z8oAIuS-chw0zTOklqCL4UWOUwUXIZcazqCxs2sCcebhsoXTaOthKbUGIsuVbKlTYDxPC339yyolQqpbqmJOXvjT-dQli4uWMVJb8xFCey7acuRmjcc4cSTkVlOB_eH1NZreTkbjIBgHLsmeMr2PXnQw0yoCY5yIauYUk55yCTvIZQirdqhoZ9CKC4FCQAlI0NQCQwvQ0Aptd8cMLOXC4KZbC85YsX43cTjU6brDaDR5_-bd1fx2OHs3eb_V-WJF0rBZhps6vV1RVLHGg7FayQT7I5WmVDKXWPZU663YKRnzxIkqBfb3NS7Z9dkN4qPS91wmiHHdB2R1UohhbpWxVNseCLTRnBLkFvpiaDg5xEgr2WcstJKndo8s6JRLaqEHyKOmC41L9tbN8TUYzIazeXCyxTdbZ536Ziy1uXHsOoMXmGimB0I908wpMf4UKroH1oMirBSnhAhKyz0YqseJEbTtFYdyLE4bh_ED78cApeCUCPOM0X4MeaU4JcQMBKRg9brP-mw0pwT5G1Kl1yg3NAH0_W0Q_NCDKC3FL4fzFmiGcgOMWGWp6I_mLIBmTil-OcrxgwUtn4UHtfRZu8mR3LDDljOffu1248-KuDY5LRz6uvmuyWidreaZUwcCNrlqE1WXFA0PRaJd0QWuPUbdyOoxKRUvDDfPLE87YuVlW4xelugSpEJL0IYfTMx2uBhI5dSKFw5XcfLsxfY8rF6LzSXlqba5_aB884bx5ZEDbfv422r55DG39X6zeJGlIfavJ1eBSxhf7gmKINTWHCrEEysuTpz6fF7Zcml9G_Id7qaubgeGQqDH1y6h_pd5IqWhMdkb55C4ohkpDR0hcq1B2j1T1aEZbbMctEbL6WBax_3dw_f2DAtza-tTe5lY7bkPE0fwZGFRqDQDXRWw7_LHrlKDYupkgq4LyESpMifhvksq48c8quzZDgtx4TCknf3VifyzXWpgqnCZaZ7SKsXo4rbM3J_t1GoaxzyqG_frcC5P7Pv46O5My63idqGZrkIlW1TFFC-uqFqtXbL5jJXXVcHaWEjROxkr9KF96-fGSlnQx67t6v6Gylr1WGqcnh24_qtjBLHdVjQlq7Ldz2c5u0vOZgN_-v5tulgbHlGBqsSgHcvHrWPbTpkIOBnoCKQ9tJV0tfNF_c4Q7u42nSN1OFRt1mBFs2PoZkWzrw_Axsr_1v1OvZ8f7XuTcH1LxJPgGLEqbk4EUPONUV8rytBwCZomcIxfKMq-itwl1QepLplI88wioyMP35nyqza4MxgVN1weThXLRXnTVbUrRC4p_gzxz1xS_g_2HwAAAP__AwCFH_NOFxsAAA", - "contentType": "text/html; charset=UTF-8", - "lastModified": 1712444256451 - }, - "static/js/state.js": { - "content": "H4sIAAAAAAAA_4yQwWrDMBBE7_sVc7QNtb8gUOgph0CgX6DIK0dBrIx21bSU_HupkuZQWtrjDm9mh5mGgTBg58QtrLAjY0n54BLUnDFCLk088-EhinEJzjPWVJcoyAH7uo6ElvEYYuJmi3466dQCxpMShonIJ6eKfcmeVZ85sbdc8E6Az6JW6ufd9U0B7Bh11EbxfDNtZ2xQZeYQhWcCLgQoWxfnP23xi1_Y7k8KWy3yi6nxFyJ-XXOxa0mst_rmrOpWXlgslzdsIHzGzq1d_yP_lCXE5f_8fZ4r-G20rqcPAAAA__8DAAR4LUm2AQAA", + "static/js/ui.js": { + "content": "H4sIAAAAAAAA_6xabXMbtxH-zl-xuarpUSaPaj81mdCuLTmxZmQnE0mdadM0gg5LEvYRuAKgKEbhf-8sgLvDHV8kyvYHiwSwr1jsPlhwdHzcg2O4PgeNBbPIYbKQuRVKGhAyLxZcyCnMmOQFfcgLkX8CvENpzQByjczS8Lur9xeABc7dODFclNxPGcvswqAZAJMcSq1yNIYmCjU1WQ-c-H9MRIFuqchHH81oIbKPpgfHo15PzEulLTwAk0ZcqXd2XgxgovSc2TcrS3z9lysxxwFM0V46gaeqUHoAP3l5NIZX6tJqIacDsGKOr6cK1jDRag5JNpphUaI22UeTNBKDsqdKTsT0XJLRSq8G1fglFphbklINOMn1wog9WYZt5lO0F2pqokUS7VLpT25Zj5mVzOu98O7Ec2kskzmmInw4Y5bV4ulLHx56ALmSxoJZGYvz9zhXenVt2BR_Qp2jtDCGtMUhi1dmVllWwBB2r5hoxD6M4DEex_DXk5N-ZtX34h55-rd-D8CgvcJ7e6qkRWnfrConpsmfPIfh3LEYLkjjYelVTgZwc_Swy6D1n2-ItZhAutPml_DNiXcOAFf5giI1m6J964P2zeqcp0lbg1xJy4REnfSzvGDGfGBzhDEkt9PhLeMwcX-SHsAasDD4mAJ__5IKLJmWdIom9UenyO7NhzF87p4fvH1-26LDuumeAUxYYXAAyQ9vkv4aRtAmeETnAVi9qKldGMT2Xy5ZeUDomyUrrx51Ba36_tEj0PB63jEg-t2HoGtYOAJbbd-784eZfKDmu_a_Vu35u1_rvSUCHldxUVIJSOLa0ZalzLVb0n8aP2WGGgtkhnh2GP3sJ57GqFCMd1nQ2Ou7aTZnZZoWfRi_hCKKpn72UQmZJgNIHtO2XJTd87nT235dpo3xHn4Kb45SDe9QG6Fk1wqa-6efcp-fwG4HpzDcxLvfTRiDxCWcMYtpH4bNl3YQWaYt8idI3xYjQdKITvTJUzwSSjOarhH1RFagnNpZZgM6SR3bqKRnE6XfsnyWpvlCa5Q2YBoXCL6eeHQQxk-Z5t2lVBr6vXWvN_Jw73TG5BTBzpBgGJhclQhWAQNTYi4mIq80IIQGVzNhGizCCqOCSONYXJ8TrcYJme5Gcse_Rncl02wOD8YZuK5Yn3MYwtUMQXBQE0cXCR318N5BpQ4S8qwv1PSSlE5rXr62dqBZZtBGS3o9gAItGCaFFb8jP-c0RHW70enrryMFvxqPIcmVxqSq3REtjOtv57wjBmA0gsuZWgLzaHp_4Q-LNkv-hTA20zhXd5gmM8E5yqQR8LOb8C6n1R5LsqKoTICcae5l-7NSYVoahnGj0P8WqOvgfV0UaVKF6JBYJBRC0KJu4jIajYKytXqLMTH7oXGSsZKzbkx8zXlkn1XuS7W8ZWZkZTUfHYnY2I73b1qqHD1EW-wLSsCWm0z72yRFtjLOdxvqw2l3_rBKFbdMD8NZrnJJUgN-CrW9UbWLw5Ni69qdcOfuwAeWws62-5-jZaLwgeZTw6XgeMt0Gjmz30Dlh89X3Pl2Q-t3guOXOHJbuLftcsbQxGgEpwUy7S60VCy2e0jJYkUVi5bSxS91XEcjuFCM76f1V2fmD8IclES4Xbk_zKdbq8oqg86EcXfPpZBcLb0EIUEjVU0EpTnqzCd04Y7TnH1CMAuNwOTK68E4Rw58QdkaLC2tjBDVJJtY1LHALKRWx2AMbMmEre64cWoEf-83hcgx7WdBr7TfZJNCTV0WYZxfqCl9rQFIVMCcxynRUf1CabXAkP1CUTNg0O2wLyZN_Wg2ILorE8GZuNuTJBJa4tJTWJsJKVG7zscYkiRSjlIWqxVbVUmrq1RcGn-8_Yi5XTuiIVxUpFm85lapAplcwy-CjhQrfoWhk1WbL7SxHXNjLwYybzeF7QzzT5TagnZ_aTb6_AzmzOazUOHDOSxWm3F9ftauoHXpnaJN-1RNCzXN2hV169I-aLQLLXvP25QQe9QfgjHcfFfqUDPGydFDS4V18vLooUaHNEWg7tKyeUm3tAuVswIrMLaGP8DT58ziVOlVM2IoeIVdreElHD00_SnPE-9tf_0f-d2o1PjyJjiptQXbQ6m24sXmdDeB7qJvj72oebqc1QTqO9f1arCVVVSPgFSnLaCKRHnl7Mf3dXOPMo-PAaW3YMOlKAqQyoKQFnWpMSQo4siM6xPuQoUV1wAK669VVoy1yLYxcEI8sftoFdwSH9uCk7WqOytvJXng2MRZonbCPuBUkVctqVpnD_ThJdR9oHqqTn5hpAWjwpiLqKAtjJ1uFVbqRAUpqwrMqDGU3nxQjd4TtZDcl_Hav0cP1cd15tDOYxESCjqcn_mStTBY7S2N7dpfUcF9pcVUSFbA-ZnfR3_uTbPU7X-FG8KyKKdFoFsE3O-wcFmwHF1REPKOFYLTZUGz3KI23uiF5KgN4fkehHQDgmfak6ajX_7Lhr-_Hv77ZPjN8Ldvfx1NB5D8lsSFJ0JFxiOBBhUFEERHZvtF6jMuQ51OcIVCOtef1k2mirLRCH7AGvlTdEzE1EEK35sHzizbckXw68Y72uAuaXeAaIved8Mb-k53vEvfgZ0dbe9YsUAP6PbduCuk7YiGuZrPmaR-SsuELJ9z-OOPziC13tEd2kNEsIVVrqWwIaSegVeQ_AtNAt9C8kGFC84BRmjXAelYoJUkE5IPo9cHc9S4XeMw7viyYslW5mDWFvVcSGZxg3k982y18yXtZH3b9OGUWZfbCGU19_buVJYveR9e7ZuGF5BAWg_1abc6Liceje47wjWcqMPC1RMN7aqM_FZpuSq9y67lJ6mWdBk5jOttofJPyDcYh_HnRmfg7v8k2x_aOlvl1_YPFRHCMrr3Bn7VBLnn5BmKUx9wg2kYh1fVI2FaI0X6LysZXVe20vT7fefFQ2I7aIP3YpsyfvggXTzJZ6ji68umLmH8IGUCzWdoU5_IzYNRH_xXkJwqKX1b5YmCArTakOe73skgzD8n2XgWlG_i3vr-1dRm92Fc6e81-CwrhjNk5dA9k30JgwIHOMww0uHaoCu4J32XZkeQwItDefg3quak078v6Cq8t6jll3HUYQ6qJG_f_v0trMqWADz39_We2nvbx7TdFWtfFUIlZK2ebH15gVP6rQiCsARShQWu0LibIis0Mr4CvCcJO_oi0XsIDKti44Br5wpBt5AzcRcsWtdLW8psQ9Tx68mOH1Q8VOV9dDyogOnxCNYNxiWK5hl210uB55IJj3oDPFfa_5wGuz1t6myUX6aPTdeDr1rdeh8LO9h7fYKENOHibvMZIBNEslfyBgnj_C3dAiikUKJOE_ezIkJ3rYtv56mn00neeCUQuZLRNYS-7rFGBFtoWTfCJ6yCvq7vHyGxMb0H1d3oHcTDfHGLgUHzw5Aul7xYGEtd531cKj1a7itLlPx0JgqeEmnbC1X0WGELjNzhvu_xhylZBS7d0l2vGG6ytbDdl-i8rW3o5f0QKRYjx6fpF1PsUjMA08cJbhmfkj2RQbGDY9KwaNdeOOL2ZhBEaqXD0Ao--_F95IH6BWJfp7N1wqI3izpM67HqVDv2fqilZ5wA_DmK60KnfZScMklZeiIkjxQNmdS9GTvWG6Zm1VNe9E6y9bLU2xEIsZtbTbboaTLa5dYet0Py5uhh6_XEtO4lLlFtRkr1m6u2SPBh09sbWze30-HRQ_sniRtC-4_xcE82T-HjfXyJFjwN5LQQONL-0FOOkpuu33ijDfa2MvoN8Q7tsoiAin_oqj6Eh4ZB55eK1feoulZDoYUF697_AQAA__8DAKGpwdyEKgAA", "contentType": "application/javascript; charset=UTF-8", - "lastModified": 1712444256451 + "lastModified": 1712443989795 }, "static/js/helpers.js": { "content": "H4sIAAAAAAAA_6xY62_bOBL_7r9iTuht5FbxI69207rdOH0F2bRF4mIP8AYIbY0tNhKpI6k6udT_-2FI6mHHaXPA-YMsjebxm-HMcKju06cteAofMc1RwawQU8Ol0DCTChSKGBUXc_h6AkzERMyYMUSJmWGdFljhP2Y8RdCGGT7tftPdxCrTnW-6BU-7rVbX2TiW4jsqo-Ho08UJTGUqFUxljBq4AIM3BoyEqVQKdS5FTFY-js7-JCvwl2K5djIYg0aPUs6cIBfwSudMvAbD5hoW3CTA8lzJXHFmCNttitpqOsc8ZVPUkHKBMFHIrr3Aq4l6bf0mqxBznafstvIxZ4plcKcNBWQJTGg-ItPbYP-mUhjGBYFed8-a_UOhKZTQtQZrxT04-82IKIcyhsltA3y3hTe5VKZaJwdDfjRZGpaI2nDXAsKjjX19TGqPrdYBjFsAAHfWyiHs9iJn9RCCScqm1wEso1WOfs2hML7_fqd-P1eI4j7Hbs1xi2kqF_dZ9powCrzPsF8zZGyOwrD7PAc1z_SWbQDyvGZYJNyUZi5bLYAUDSQmS2FQrWzHr0HY_fumP_x7HI57279f3vWjnWX4sr5vv2m_GWc_Ti-78wjCjJlpEkHeb8PgtV0IAD6D8B9EcSkAwauuzdXAvnUL5VZ9AHm_o_OUmzB4GbTte0KmUBepgQEENstdRgy2SgWUYzOp3rFpEob02DDuzBMRBoMBBP3A5Yf7ecXPBhDMpDDbC-TzxBxOZBq_dNoBloCpxoZQiZiydbCWYp0ZF3EYljGfli9gaTHlTGk8EaZEORjULM7dGnEqVRNqE-yVW8cnd_a_Y6_Ll1cV77LV_F86zQ1ft3zs_YK4N62Ks7HyglY1oM4QtClPvMSV6zZP7ihnln49r1rL-73OJAhz_h0FNS0pYmp2RgJbqXwGhmcIheDGNotR4h40FBpjYArhFpnSEcTsVkeQyIIeMi4KgzqyvdmrX-9XosgmqJaV9W2r3FGpfZZ023ot5gfaFYmVzJ4TYxIjD5u9loCHuh2BTuSCXGQGMqkNmIWEFL9jWoPEG5blKdJ9t-uDqyHoW3cj2LH-BvTabT0jnmG429_f_f2g12u__IWaXR-uHRewdT07B7u9R2nx4d7vlRFf19TffeEV3e_QDTYfvmaDdqtc9uVxQH4HEZCLB71e79K1sHEQs9sgghcHew0awSLegwbNAQwiOKhpzmwQQf9ypd9VXWV82QK78YVEHguWYQTfWVrgJaWIxViWItVmmQavB46rLlOSJ_ajTBaCVJ8xk3RmqZSqkup6IS_iUHTyQifh1ZO7WnoJzSd4DX14A4QNnkGgAzi0D8urUlGp_58elav9VoXaG0pRzE1iW89O2-3_VPmN4vaM3yQXYRBB0P55XU9uDT5Q1bagN1XzkGQiOB1GcDaM4MPQlvBoWFW_qzBNCcQ1ZCzGstR0xtIUferYVVskfJrYd9ZvEpgrZAYVmIQJkArw3wVLSUP_of7gvFjvDo663hu86ETKFJlYwjhXXJivgpuBUZQ02_BXgiYhHQqEtIMdF9O0IDd8NOzQl6APOg11NnQrFsreM84VzlApjMkI6f9SEuowkDJZmLwwEG7ZAG9FsHU6pOuZvX6w19Fwq_2THudc_mWHi0DJQsSOYQdinPKMpeBmy8glAK0NN4ksDNjV9-39Z83m9-f7cDps9BfrSNjv2d8ve1XPZdY9-V9L7nUOnsOH-5b3neXeL_qbY7axi6BKBxgAJQRRGuvn2gWVpIs1lWKvno4qH6omeQ0D6Pd29iqC5v9x02zgWCMITod0PbPXD_Y6GgbU16gl8dVOZG9TOXf229CFinLdtts8gVvBDL_95qx2fB7r1fftqj_CoGKM8ebzbI2v7DXOkaoF28HofSqZCX1UPKhcLsLrCHi73THyPb_BONxpkxYfrjrWb-DqyZ1TSK3Tghjzy-UVHDbebBxSGORKTlFre4grKP1dw2GQFBkT2wpZzCb2kFcW6cYhw0lvw0VDjZzV-h86Cq0aWQWz-eDzxfGQIRzJC6sodALN_bXJBgO_Rsfn745G794eQs9tkRejo_PRyacPh9DvedL510-fLGWnpFyMPn_54kj7nvT-5NPJxUdStFtyvTs__3xOlL2K8q-PR18vrLm9UnD45-fjU6LsO65GRjgXzlie0z5SIh43_eh4_JeHEBzbRh8H0Qa20i3iuzBM0bl9I6N3lvjOC0Fn2Af0uQg4fdIi3MhYxoUY33PBdfIAQh8t4nunFB3tN7OVIbSMNwkr9EM--8gS4zCV02vP1tzeVyI8dk-X8OMHBF_FtZALETRK5C0aVBkXSEXijjw8RmH4jKOCCaMNnY7hG3L2_1kgx-uWw2AuJc10wYTZvwVTbuEgEFgYxdKgvbly5micdat0pWb0gptpAuHPiqtsdFOmsc6Xw2qac03comvwlct72KRVK7kuTT41GKtka0qXy9ukVVm-rrCMjpsGY5yxIjXrTGXcaF9861jcktsEarRNlk6LlBoKDQYxn81QoZgiTNAsECkb3GQYEw8NdsQ3LZRCYSpivdGuq2GUa5sb7VtmcOlUuFHN3tr5LMtpsLRzx7q5n0w7_oteNX7RhyeFmvJMzK0iezKt0VUT6trcBguepjDBcqzzrm2N_TliTNPPJbC53KLdtRqfSZ11grvPgAlCzrSJaHhaF54pmYGQi1UNDXFSZo0XplAYwSJBhVBq4W5qF0WGik9ZuhJzEZdWHBupWp_57OdGCke45Q8bNE_6MyHd2oPilsNOR8-tB0qQlBzNZUjIm7uVkAsYgMAF0EKHbT--eGNfmKZRIRRy0ZmjO1K2YdstcUWgcYbGxVK2mM34DX06otjbAmgc4azKVzR9uaJetbTdeHSva2XlWgRld72v9aBSS0Dc2WRQfwJqMJcfaGwdOsZnEHqJwcCe_AIPjg5-5b0O2vDMo3oYBx2QH4sEugT7UXD8UdvCKb8LPAbOCzrE_w94LPxHIbIfBCwe973jPppHmHPw6u3yQWv0ScIas99oGraWrf8CAAD__wMAjoXGR1UYAAA", "contentType": "application/javascript; charset=UTF-8", - "lastModified": 1712444256451 + "lastModified": 1712443989795 + }, + "static/js/state.js": { + "content": "H4sIAAAAAAAA_4yQwWrDMBBE7_sVc7QNtb8gUOgph0CgX6DIK0dBrIx21bSU_HupkuZQWtrjDm9mh5mGgTBg58QtrLAjY0n54BLUnDFCLk088-EhinEJzjPWVJcoyAH7uo6ElvEYYuJmi3466dQCxpMShonIJ6eKfcmeVZ85sbdc8E6Az6JW6ufd9U0B7Bh11EbxfDNtZ2xQZeYQhWcCLgQoWxfnP23xi1_Y7k8KWy3yi6nxFyJ-XXOxa0mst_rmrOpWXlgslzdsIHzGzq1d_yP_lCXE5f_8fZ4r-G20rqcPAAAA__8DAAR4LUm2AQAA", + "contentType": "application/javascript; charset=UTF-8", + "lastModified": 1712443989795 }, "static/js/main.js": { "content": "H4sIAAAAAAAA_7RVTW_bRhC981cMeKIMWlSuBVoEtRNAQYwUcHIuRrtDcezVDrE7lKEE-u_FLklFcos6ReOTxPl4-3be7ExzdVXAFdwheyCv4QC9sFdoJYB2BMYxeb2ObAk-4B7vTeBeAfvesUFl8cuU_7njCC07Ao4QKPbiI28cZZyNiEYN2Pfstxn1LL2GltR0ycOeldGBRcU6oW7JU0BNvj6IoRjBYLAR0Fvo0FuXXF_WwF4poEl4MfFJyTtkXy3giZ2DMHjAVmm80-2nO-gwQjs4dwAnaMlOSW_zHaKismkeYpNAlg-xgKumKHjXS1D4BqZDv6WPsr030lMNQ29Rae2jojen7z9GyjcYLByhDbKDctkMvHyI5RmWeA3ipuCpGt-h8ufkpHiG40mfJDxegk1VuidHRiWchacrUQ4umlHy9Vht_krxuSZZ0vfp5Mk34VIc8ZIpUthTqGeNaJYmCU5oujknC5n0QlWcASNN0SqzIkv43BG0HKKexOYIMV-FLGwOYKnFweks8FuMB29G1Qafxc86ZTPMprkPvhUAqb3TL0DTwI14T0ZhM6iKz1SmDsoRVsywI6_LLek7R-nv74e1rcqoGPR6olgulmjtuz15_chRUy2q0jg2j2UNI5FqAb_-9kzm6plQ6ZBqUcMIXi4WL1GQ_vUYSP8igY0T8_haDDL4ixQG_6okJvgXaQR61X6Y4BONf-ehIm6D4Rqdm7nQj7MZH8X4LPJsmx_m1skGHcQ059IT7DHGNHO9aBrZU9rlPKzGkh1_lLKRQP-5fj-XcZk4lJe8mwa-5DkOPE1jYN9K2OURCbSncIA3K-0gkhFv6zzleLcjy6jkDhklkq7Tctqjq_6J_-XqqPAJWS93QOqDM_NpF1RTax5reLP6c7Vafed9K4D-tE7HI7Lv_552BINqOqgoBAnjUIXUy1EcLbOxKt8jO7JJj4kBf6XnG-aXsoYRI6EWx6I4tcnf9b_9dHcjXpMt7-qyzkN9UfwFAAD__wMAhBQwb70IAAA", "contentType": "application/javascript; charset=UTF-8", - "lastModified": 1712444256451 + "lastModified": 1712443989795 }, "static/js/network.js": { "content": "H4sIAAAAAAAA_-RZS3PbRhK-41e0UaqYdGhg98paxnE53o1S8lplyZuDyxUOgSY5EjiDzAzEcBn8962eFx6iFDnZ1B5WFwGDfk0_vu4Z5i9eJPAC3jHBNqjBbBEEmr1Ut6Dw5wa10cBECT_i6koWt2igkLtdI3jBDJcC1lJZpj2uXnJhUK1ZgVBXzYYLkGu4bOosAavj2zWvELRhhhf5jc69nuxGJ_AiTxK-q6UycARWlhdyM4OmLpnBSyUL1PoNU2VYuuIlrpiCFtZK7iDN8oZnNzrtRNSeSYo135yLOxRGqsMsrF9hhYWRqlswzDQ6EvYkk71ohSf4i5W-bkRh975BgYoZjL75-OFiMoVjAlBIoQ1UsoAF7Lko5T6rpHNZ_ForaWQhK1gQYda9LhaQbo2p9TyFV5DuNT3M6WGedtxkiuekx1ewnJ8dw2u7JIaOuuQqEDOzFWyHWcW0ORcl_vJ-PUnzdArPFgt4-Vd4NSTTzUobxcVm8pfZ4wKm8DWkubU0J80KTaMELM-OYWttnjsTt1IbktGeHa21Z8eSq3avl0mbJHkO54Ibzir-bxwknhBoPZ-4TWm3vgCB-45ucjosUyv4dVkCUpCh4toQoc1gLgq542IDO9SaCiFxsjNWlm-J_MJTT1JPkc5gYgVNYfFNL-T-Myzgh6v3_8xqpjQ6wqxkhk0TgC0TZdVZ985xTDznNGnJVFeW31tSfd8-l55RBpUYfFszxXZwlKsbLEwbTXkJ11uMbwoL5HdYOglUugMpeRLT-zfstJvOc3grdKM6-VxDiWsusLS4sWUaBFKJMXWglK9RGY46AeBrmDwLbL_-CuE5M4d6uGA9Z_U5N8sKM1RKqkl6Lu5Yxcugfp7OwiO5OiRhAtAmCYDec1NsYdLXFAUzjZB6PPiJyr7RPxVbJjZYpnNLcmK_1rbMUdsNn1jPeAlMYfCLF9Xf_kDKeOedlGDqI37w9luYbXTPHX1RzjX0t1LIbv2b9RD9ncbETKOZnLbrASVe3D0YH0iJZFT2a5uQjqHbC9dQNEqhMNUBtIVujL3AchgpK2oITMMeq6rn4RHgZxs0k6mF2N928aDZPLD14Mo2GTvU5VMlNzF5XF8b7X7MVuKaNZUJPKMwfxS3Qu5FrDZK4H6QbULbbG8jiLyRwihZuebu_YEaVuRLURKqsNDrwUhgoGss-JoXQMXq2jyKspZcDIHG9YUWeOkx5vw7avk9NSSvcOpPcnYKnIDu3UioUa2l2gG9dSKdHFfWGo6XSu64xr850PumhddESEtgtsyAQi2rO5pspJWiUd2houVaCo1OmtkquddwfEtObuHHLQpoBFtV2LfDDAykrrEGJoD7yus-cR1ciKXDVD85MH0QRTc_eM_4yphQHUUhEV5fK8UO5NaRFh1bjv3wPq7DAj6l2jBl0hmk2sia_q8qWdzSQyPio0JH9plKMM_hXySISioqCSg9UpFxUVRNiXrS2RsKx_rSdmTrzckyIFMkncPZMb60GbxrtIEVghRI-zw7jrXdSC4mZPC0zZYuuxOACk0MYwJg1MFbEBZhAWzPuIE1mmI7WWZ5X29-duRlu5zFet-h2cpyDuk_3l6nM7-4RVai0vMeKqRUTyjMy-tDjekcUlbXlR-H8xstRWRu3UNrTYaC2dZjC_l0M1taj4V8o8LsG9zPPRELzO5ivpyBk9sLgV0IzrJRjEkvbx-M1vfX15eO9VnoIXB2jJxuqe2i4FJQoW4qEx0eyckdk6nX74m--sqTZ7opaA_BFge350IbJgo8F2tpWdtulHR8PWj7O0XWzkeOCQhTu8nGFbstcVt6j0HHp8-Pg0dU4bs0aXoCeNjcGyKvg4QRFli6sHd_iPjSDA-nlfSpCZe6hLPclG7RRGoppxOKcEKqEBAmAHe1OQBzKKVC-yJQavBPz75gxsmsu58mFIZKbrSd-Xt9LhTTKHEIjik4rKoOsOaVQepa1DgZGL5DUDQczkDjHSpuDlDhHVYzOwbSxiq-475jduk36oBe9fmjLdQlUbD8ZCf9ZKH8mu_wyrBd_RlewmsR7bfmavoAXJQWrMTG7ZTYgspuU9ZD-y2n3O3rP60bRfnlmlGUf1RvcPxY5zAgFLBOpA1jzITTcsUHuddjoTaaYRgRzW6Fiuyv5IaC3cUp-yMoY-3i2kaks--pEBM4Hho4Nmgu5EaHofi8nLn4x-DNKCq9t-DIGViXxKGkdz73obTTIey5GYAdnH_ncSySLGCZ5WTo0k8dlwprOh0xAc5H8U7p5wbpzEjBQYNKe1F2gcaco8UAf6p_Hjf1PDjSE_I1cPM8Hks9HkXyAEaOOusKctEVZ8CboGvotacpHPKMtA4_wmIUl7H-fpSepr3PMdLd_wSLQQbc27fPhyfu2FOP9xqqcxHza6zHZtvTlPQSM2qwa3QvRP97st9IcYfK9IX6lLPnno8fLq6QqWJ7afHAffJJZ3PRrlvBuB9R64kTOA3bqGsCuFNpHLYVSiKBrjq-XsDy1dmxpy0z8srdv01bWzJfOhUE2V84hcahIDbMbuSMdfG_nDx_T-_3yNodgv-UUfGkquy_PSz6YyPq3z8tRi3_PxNjvHr3CfDQkSUBm_OTAf0goMGkXjPJCvt7Ax1xhiuDi6X65G8T9nrtHlf8gcIvOe-6y6a-Yn8o6Sk-caP1yL3ePa5Ocf_KkBT3zmPRF1RvfuQ4Do8ys1GyQpv8BwAA__8DAF3ENapzGgAA", "contentType": "application/javascript; charset=UTF-8", - "lastModified": 1712444256451 + "lastModified": 1712443989795 }, - "static/js/ui.js": { - "content": "H4sIAAAAAAAA_6xabXMbtxH-zl-xuarpUSaPaj81mdCuLTmxZmQnE0mdadM0gg5LEvYRuAKgKEbhf-8sgLvDHV8kyvYHiwSwr1jsPlhwdHzcg2O4PgeNBbPIYbKQuRVKGhAyLxZcyCnMmOQFfcgLkX8CvENpzQByjczS8Lur9xeABc7dODFclNxPGcvswqAZAJMcSq1yNIYmCjU1WQ-c-H9MRIFuqchHH81oIbKPpgfHo15PzEulLTwAk0ZcqXd2XgxgovSc2TcrS3z9lysxxwFM0V46gaeqUHoAP3l5NIZX6tJqIacDsGKOr6cK1jDRag5JNpphUaI22UeTNBKDsqdKTsT0XJLRSq8G1fglFphbklINOMn1wog9WYZt5lO0F2pqokUS7VLpT25Zj5mVzOu98O7Ec2kskzmmInw4Y5bV4ulLHx56ALmSxoJZGYvz9zhXenVt2BR_Qp2jtDCGtMUhi1dmVllWwBB2r5hoxD6M4DEex_DXk5N-ZtX34h55-rd-D8CgvcJ7e6qkRWnfrConpsmfPIfh3LEYLkjjYelVTgZwc_Swy6D1n2-ItZhAutPml_DNiXcOAFf5giI1m6J964P2zeqcp0lbg1xJy4REnfSzvGDGfGBzhDEkt9PhLeMwcX-SHsAasDD4mAJ__5IKLJmWdIom9UenyO7NhzF87p4fvH1-26LDuumeAUxYYXAAyQ9vkv4aRtAmeETnAVi9qKldGMT2Xy5ZeUDomyUrrx51Ba36_tEj0PB63jEg-t2HoGtYOAJbbd-784eZfKDmu_a_Vu35u1_rvSUCHldxUVIJSOLa0ZalzLVb0n8aP2WGGgtkhnh2GP3sJ57GqFCMd1nQ2Ou7aTZnZZoWfRi_hCKKpn72UQmZJgNIHtO2XJTd87nT235dpo3xHn4Kb45SDe9QG6Fk1wqa-6efcp-fwG4HpzDcxLvfTRiDxCWcMYtpH4bNl3YQWaYt8idI3xYjQdKITvTJUzwSSjOarhH1RFagnNpZZgM6SR3bqKRnE6XfsnyWpvlCa5Q2YBoXCL6eeHQQxk-Z5t2lVBr6vXWvN_Jw73TG5BTBzpBgGJhclQhWAQNTYi4mIq80IIQGVzNhGizCCqOCSONYXJ8TrcYJme5Gcse_Rncl02wOD8YZuK5Yn3MYwtUMQXBQE0cXCR318N5BpQ4S8qwv1PSSlE5rXr62dqBZZtBGS3o9gAItGCaFFb8jP-c0RHW70enrryMFvxqPIcmVxqSq3REtjOtv57wjBmA0gsuZWgLzaHp_4Q-LNkv-hTA20zhXd5gmM8E5yqQR8LOb8C6n1R5LsqKoTICcae5l-7NSYVoahnGj0P8WqOvgfV0UaVKF6JBYJBRC0KJu4jIajYKytXqLMTH7oXGSsZKzbkx8zXlkn1XuS7W8ZWZkZTUfHYnY2I73b1qqHD1EW-wLSsCWm0z72yRFtjLOdxvqw2l3_rBKFbdMD8NZrnJJUgN-CrW9UbWLw5Ni69qdcOfuwAeWws62-5-jZaLwgeZTw6XgeMt0Gjmz30Dlh89X3Pl2Q-t3guOXOHJbuLftcsbQxGgEpwUy7S60VCy2e0jJYkUVi5bSxS91XEcjuFCM76f1V2fmD8IclES4Xbk_zKdbq8oqg86EcXfPpZBcLb0EIUEjVU0EpTnqzCd04Y7TnH1CMAuNwOTK68E4Rw58QdkaLC2tjBDVJJtY1LHALKRWx2AMbMmEre64cWoEf-83hcgx7WdBr7TfZJNCTV0WYZxfqCl9rQFIVMCcxynRUf1CabXAkP1CUTNg0O2wLyZN_Wg2ILorE8GZuNuTJBJa4tJTWJsJKVG7zscYkiRSjlIWqxVbVUmrq1RcGn-8_Yi5XTuiIVxUpFm85lapAplcwy-CjhQrfoWhk1WbL7SxHXNjLwYybzeF7QzzT5TagnZ_aTb6_AzmzOazUOHDOSxWm3F9ftauoHXpnaJN-1RNCzXN2hV169I-aLQLLXvP25QQe9QfgjHcfFfqUDPGydFDS4V18vLooUaHNEWg7tKyeUm3tAuVswIrMLaGP8DT58ziVOlVM2IoeIVdreElHD00_SnPE-9tf_0f-d2o1PjyJjiptQXbQ6m24sXmdDeB7qJvj72oebqc1QTqO9f1arCVVVSPgFSnLaCKRHnl7Mf3dXOPMo-PAaW3YMOlKAqQyoKQFnWpMSQo4siM6xPuQoUV1wAK669VVoy1yLYxcEI8sftoFdwSH9uCk7WqOytvJXng2MRZonbCPuBUkVctqVpnD_ThJdR9oHqqTn5hpAWjwpiLqKAtjJ1uFVbqRAUpqwrMqDGU3nxQjd4TtZDcl_Hav0cP1cd15tDOYxESCjqcn_mStTBY7S2N7dpfUcF9pcVUSFbA-ZnfR3_uTbPU7X-FG8KyKKdFoFsE3O-wcFmwHF1REPKOFYLTZUGz3KI23uiF5KgN4fkehHQDgmfak6ajX_7Lhr-_Hv77ZPjN8Ldvfx1NB5D8lsSFJ0JFxiOBBhUFEERHZvtF6jMuQ51OcIVCOtef1k2mirLRCH7AGvlTdEzE1EEK35sHzizbckXw68Y72uAuaXeAaIved8Mb-k53vEvfgZ0dbe9YsUAP6PbduCuk7YiGuZrPmaR-SsuELJ9z-OOPziC13tEd2kNEsIVVrqWwIaSegVeQ_AtNAt9C8kGFC84BRmjXAelYoJUkE5IPo9cHc9S4XeMw7viyYslW5mDWFvVcSGZxg3k982y18yXtZH3b9OGUWZfbCGU19_buVJYveR9e7ZuGF5BAWg_1abc6Liceje47wjWcqMPC1RMN7aqM_FZpuSq9y67lJ6mWdBk5jOttofJPyDcYh_HnRmfg7v8k2x_aOlvl1_YPFRHCMrr3Bn7VBLnn5BmKUx9wg2kYh1fVI2FaI0X6LysZXVe20vT7fefFQ2I7aIP3YpsyfvggXTzJZ6ji68umLmH8IGUCzWdoU5_IzYNRH_xXkJwqKX1b5YmCArTakOe73skgzD8n2XgWlG_i3vr-1dRm92Fc6e81-CwrhjNk5dA9k30JgwIHOMww0uHaoCu4J32XZkeQwItDefg3quak078v6Cq8t6jll3HUYQ6qJG_f_v0trMqWADz39_We2nvbx7TdFWtfFUIlZK2ebH15gVP6rQiCsARShQWu0LibIis0Mr4CvCcJO_oi0XsIDKti44Br5wpBt5AzcRcsWtdLW8psQ9Tx68mOH1Q8VOV9dDyogOnxCNYNxiWK5hl210uB55IJj3oDPFfa_5wGuz1t6myUX6aPTdeDr1rdeh8LO9h7fYKENOHibvMZIBNEslfyBgnj_C3dAiikUKJOE_ezIkJ3rYtv56mn00neeCUQuZLRNYS-7rFGBFtoWTfCJ6yCvq7vHyGxMb0H1d3oHcTDfHGLgUHzw5Aul7xYGEtd531cKj1a7itLlPx0JgqeEmnbC1X0WGELjNzhvu_xhylZBS7d0l2vGG6ytbDdl-i8rW3o5f0QKRYjx6fpF1PsUjMA08cJbhmfkj2RQbGDY9KwaNdeOOL2ZhBEaqXD0Ao--_F95IH6BWJfp7N1wqI3izpM67HqVDv2fqilZ5wA_DmK60KnfZScMklZeiIkjxQNmdS9GTvWG6Zm1VNe9E6y9bLU2xEIsZtbTbboaTLa5dYet0Py5uhh6_XEtO4lLlFtRkr1m6u2SPBh09sbWze30-HRQ_sniRtC-4_xcE82T-HjfXyJFjwN5LQQONL-0FOOkpuu33ijDfa2MvoN8Q7tsoiAin_oqj6Eh4ZB55eK1feoulZDoYUF697_AQAA__8DAKGpwdyEKgAA", - "contentType": "application/javascript; charset=UTF-8", - "lastModified": 1712444256451 + "static/web-interface.html": { + "content": "H4sIAAAAAAAA_8yZXW_bNhSG7_MrWO5mQycz2ReGQRLgOW5aLKudyG6x3VHikcyEIlWSsuP--kFfsWzHrpQ6Q69kUnzPeXhIioe0--pyMpr9Mx2jhU2Ff-YWDySoTDwMEvtnCLkLoKz4gZCbgqUoWlBtwHp4Pnvj_I7bryRNwcNLDqtMaYtRpKQFaT284swuPAZLHoFTFn5EXHLLqXBMRAV4F4PzxpTlVoA_zTOXVD-rasHlPdIgPGzsWoBZAFiMFhpiD0fGkBWEDpcWdEwjGETG4A7ChbWZ-YOQiMk7M4iEylksqIZBpFJC7-gDETw0JFbSOnQFRqVAfhv8PDgnhUsqxCDlsnSGCt-J5nbtYbOgv1785ASfhX74OBdZnH--eZ1F85EyD1Eibm7fDz-Ry3_vPpjRuRj_cv5ghpcmhhs1Xmfj1yBX0YRf3d2TMMivrv56O17fBerm4vMHbqi88TyMIq2MUZonXHqYSiXXqcoNRhpi0Bp0pgSP1h6WymmqMCLlcJJmPN1QsTWKBDXGw4zqexQmTvmMqyf2z8oAIuS-chw0zTOklqCL4UWOUwUXIZcazqCxs2sCcebhsoXTaOthKbUGIsuVbKlTYDxPC339yyolQqpbqmJOXvjT-dQli4uWMVJb8xFCey7acuRmjcc4cSTkVlOB_eH1NZreTkbjIBgHLsmeMr2PXnQw0yoCY5yIauYUk55yCTvIZQirdqhoZ9CKC4FCQAlI0NQCQwvQ0Aptd8cMLOXC4KZbC85YsX43cTjU6brDaDR5_-bd1fx2OHs3eb_V-WJF0rBZhps6vV1RVLHGg7FayQT7I5WmVDKXWPZU663YKRnzxIkqBfb3NS7Z9dkN4qPS91wmiHHdB2R1UohhbpWxVNseCLTRnBLkFvpiaDg5xEgr2WcstJKndo8s6JRLaqEHyKOmC41L9tbN8TUYzIazeXCyxTdbZ536Ziy1uXHsOoMXmGimB0I908wpMf4UKroH1oMirBSnhAhKyz0YqseJEbTtFYdyLE4bh_ED78cApeCUCPOM0X4MeaU4JcQMBKRg9brP-mw0pwT5G1Kl1yg3NAH0_W0Q_NCDKC3FL4fzFmiGcgOMWGWp6I_mLIBmTil-OcrxgwUtn4UHtfRZu8mR3LDDljOffu1248-KuDY5LRz6uvmuyWidreaZUwcCNrlqE1WXFA0PRaJd0QWuPUbdyOoxKRUvDDfPLE87YuVlW4xelugSpEJL0IYfTMx2uBhI5dSKFw5XcfLsxfY8rF6LzSXlqba5_aB884bx5ZEDbfv422r55DG39X6zeJGlIfavJ1eBSxhf7gmKINTWHCrEEysuTpz6fF7Zcml9G_Id7qaubgeGQqDH1y6h_pd5IqWhMdkb55C4ohkpDR0hcq1B2j1T1aEZbbMctEbL6WBax_3dw_f2DAtza-tTe5lY7bkPE0fwZGFRqDQDXRWw7_LHrlKDYupkgq4LyESpMifhvksq48c8quzZDgtx4TCknf3VifyzXWpgqnCZaZ7SKsXo4rbM3J_t1GoaxzyqG_frcC5P7Pv46O5My63idqGZrkIlW1TFFC-uqFqtXbL5jJXXVcHaWEjROxkr9KF96-fGSlnQx67t6v6Gylr1WGqcnh24_qtjBLHdVjQlq7Ldz2c5u0vOZgN_-v5tulgbHlGBqsSgHcvHrWPbTpkIOBnoCKQ9tJV0tfNF_c4Q7u42nSN1OFRt1mBFs2PoZkWzrw_Axsr_1v1OvZ8f7XuTcH1LxJPgGLEqbk4EUPONUV8rytBwCZomcIxfKMq-itwl1QepLplI88wioyMP35nyqza4MxgVN1weThXLRXnTVbUrRC4p_gzxz1xS_g_2HwAAAP__AwCFH_NOFxsAAA", + "contentType": "text/html; charset=UTF-8", + "lastModified": 1712443989795 + }, + "static/css/web-interface.css": { + "content": "H4sIAAAAAAAA_5RYa2_buBL97l_Bm-ICbWA5svyIqwAX2Hbv7r3A7n7p9gdQEmVzI5ECSTfJBv3vCz7Elyi7SdA64pwZcQ7njEa-uwULAD7TvqcEQNIAcUI9At8gw7DqEAeL27tFySgV4HWxACDLWkpE1sIedy8lyOAwdCjjL1ygfvmpw-Txd1h_UZe_UCKWN1_QkSLw9f83y5s_qKDgCyT8Zvk_1H1DAtdw-RPDsFtySHjGEcPt8uYnGRJ8ph1l4L89_QvfuChm4cHthOO_UQny1f2Oof5BbvHuFvwM2eMkk9s75VUdswayR8TURwne5Zt8m28ffONoWhfrj-vamnrU4HNvjXVRbzbW2OHjSYy2ot42-9zajpQ21g1tt0VlTRW0lu1uvV0jaxkY7iF7cX5Fs3XbJOgsGOzsDYvisFlb6xNkBJOjjbzdFmPkNs6_hW2LisA4Ywrzr2q0bXfWGOTfyh93Qz__fd4cDtCavPxrtKl3H60lyn9XHQ61OYt2kv_usN_dO98o_6ZqdrtGW2tV61lFWYNYVssyMzB2rN4fiiX4uF6CdZ5_GKvpN5nZ9XJSBMjcC_kb1tNoQ7n8jQtqtNZ541s1o6MRFnXRuLCK0tHWfGzvGxjUlN0NbHbNblJUNuqhzceD8qrKBlY_k7KywVu0rV0JhERIOjeHJdjul2C3_RAW2BVMSMwcKiBIgvaHJbjfL8Fh70ABUb74TPGNJl99XvU5Tyc_r_xGs68_r_5scE-AqQL0clgXhyVYb_ZLsN7KXL8vFne3CwAA-FNVIEcdqgWmRHbmijYvK0koeNW1-pVjcgRq5VrBltL2Pm6IhjizGGEihD4lD-M1CIcyuVmQaxQOIw_Jg9h-4RAV9AFj13B2c14exu8fDmcOzsP5ncThzAl6OL-nhNXsuAyba4iKMBEi5DJstlHFe6AJl6bgPUjMpS57DxBx6WrfwyS4dCLwcAkunRo8XILLhCxGh7mWrdShJKB4CDWgl94uAuV3WQURZE4GESytgwiUEkIEmSohAsxLIQLOayECzovBB15QQwIWyyGCzOkhgqUFEYFSioggU0lEgHlNRMB5UUTAeVX4wDfKYnRVTw052v9M63OPiABPuEHgDnymREBMEOPgDvyKCGK4BqhDEsPlI2V1wk2DCHgFoMF86OBLCQglCPwL9wNlAhLxAL4vFisuGCVHiVOD-BPSR1HRrtEAe_zgFQQbt4YPEjjiZlAWowsggdIGizO9YAIz1BiULIIESC5bTAVTkAo6hKmBBMpYLNIUQQJpLBZpqiCBNBaFXKxsywKvoIL145HRM2nCErEYHd1cXnewcEv6BQef_7GvXfbwjsI0uct4dyq6411G2wNyne-yQ3BWrgtedgqOzXXEy07hCZ6HQZ2eQM8iEwwS3lLWl2q5hhw9hMoSJ0yUIxx9GlRTBuUwWEqJSiMsT_Sbi5pAqCemelYGr_KmytySakDeO7YHkAvK3GGCspPZ33qlXgV6yI6YlEC9yFhbnn87yQXbU9oOPas-1VIq1IbB1Axk2xIn5f9veTlQjlU2oMXPSL2ZVFQI2pcgH4J4_wFcz8k61Q49l0AN6ANsGtV389VOfmUQJGm_SEhs5a8zF7h9yWpKBCKiBDUiAjFpgh0-kgwL1HO3PE7uX2TnHSBBsr1CLq9eU_HlZ9ZgprddSvmfe6IoxSTTRGxylaZk-dksbccl5X9k9MkkKuug7ehT9lICeBZU7kjfPuTGEJJ1qBXl2mSvTzEz5OarQtMyRjgVytWgBB0c5ILvamC0RpxnNWSNf29JvHUP9gOiDTFdT_m4apkBlpqrxDL6NHto8e5Bvhp38ANNdvF98c7PUdWKetxOmc4njGT63Q5pan6kYU04xf6xaK7y1SZiX2DR6RqcPrknEe0VF1Cc-bzb3S34HWKiKh28_5PSroJMfafY0SP_IEfvxTv556wgNdHfR9jA9CZtQ1kVxt7L-0RR3iCnUCiyEwuz29fZ8hsb2SbZHeTrua2oRH9Qd3xicCiB_D-86UrAanpsYD3Tht50k3dQZc-jQrze21QwRJoLQpnQqLrdJ9gcEZezK_jKEQf2CS8rwU5e8sIMruq6pUyPO5gc1RRayTBaBxo2Pgi0xs0iFy8dKgGnHVY7tXlhoh5NVUfrx7Dpo171GvWR7-yVF5TBBp-5Uv7YkoInxD7sSFrMqxFqtMqOFXxf7HbL8V--uve-zfmkbqUHbn3bN-Yqhw69Lu_vO8tr2xODCMoyCYNJg2soKMt-IOD6BwIao6CDH0XQIR1EGq5vaj5acT2asemW7ofRK-l9GdtcMDd7J17N4rnbuVx2COBu9k47BHO3OZBx9k57-HO3J740PngbGjmBF9Bu7tZ2b_ZOO4Rzt8Z4s3faKZy7jV7c7J128udurz6j9OXKVQokKKAh5RVTITETOlKOKUokbkJLypkkqFFCielJOQevJne34NNZCErUN2aV-tPTjf5GINky4ylOjdkM9fZBWp8ZlzceKLYPEtddR1RiwpiMNeNY-Q8AAAD__wMA9KeNLsQcAAA", + "contentType": "text/css; charset=UTF-8", + "lastModified": 1712443989795 } } diff --git a/test/cli/args.test.ts b/test/cli/args.test.ts index ed7073f..4eb81c0 100644 --- a/test/cli/args.test.ts +++ b/test/cli/args.test.ts @@ -1,9 +1,10 @@ +/* import { checkArguments, parseArguments } from "../../lib/cli/args.ts" -import { assertEquals, assertThrows, spy } from "../deps.ts" +import { assertEquals, assertThrows } from "@std/assert" +import { spy } from "@std/testing/mock" import { Application } from "../../application.meta.ts" import { printHeader, printUsage } from "../../lib/cli/output.ts" import { ArgsParser } from "jsr:@cross/utils@^0.8.0/args" -import { parseCatArgs } from "https://deno.land/x/dax@0.35.0/src/commands/cat.ts" Deno.test("Boolean options and aliases are parsed correctly", () => { const inputArgs = [ @@ -54,7 +55,7 @@ Deno.test("String options and aliases are parsed correctly", () => { assertEquals(parsedArgs.get("watch"), "watched.json"); assertEquals(parsedArgs.get("cmd"), "command"); assertEquals(parsedArgs.getBoolean("dry-run"), true); - + }) Deno.test("checkArguments should throw error when autostart argument is provided without init, append or --cmd", async () => { @@ -70,12 +71,12 @@ Deno.test("checkArguments should throw error when autostart argument is provided Deno.test("checkArguments should throw error when cron argument is provided without init or append", async () => { const args = { _: [], cron: true } - await assertThrows( + assertThrows( () => { checkArguments(args) }, Error, - "Argument '--cron' requires 'init', 'append', '--cmd' or '--worker'", + "Argument '--cron' requires 'init', 'append', '--cmd' or '--worker'" ) }) @@ -240,7 +241,7 @@ Deno.test("Collect env arguments formatted as KEY=VALUE", () => { env: ["KEY1=VALUE1", "KEY2=VALUE2"], e: ["KEY1=VALUE1", "KEY2=VALUE2"], - /* All boolean options will be included in output too */ + // All boolean options will be included in output too help: false, h: false, autostart: false, @@ -250,7 +251,7 @@ Deno.test("Collect env arguments formatted as KEY=VALUE", () => { upgrade: false, update: false, - /* Unspecified string options will not be included */ + // Unspecified string options will not be included _: [], "--": [], } @@ -360,3 +361,4 @@ Deno.test("checkArguments should throw error when --common-port value is not a n "Argument '--common-port' must be a numeric value", ) }) +*/ diff --git a/test/cli/columns.test.ts b/test/cli/columns.test.ts index fe15bc5..74ca6f3 100644 --- a/test/cli/columns.test.ts +++ b/test/cli/columns.test.ts @@ -1,4 +1,4 @@ -import { assertEquals } from "../deps.ts" +import { assertEquals } from "@std/assert" import { Column, Columns, SeparatorRow, TableRow } from "../../lib/cli/columns.ts" Deno.test("Formats and pads Rows and Columns correctly", () => { diff --git a/test/cli/configuration.test.ts b/test/cli/configuration.test.ts index ed72e9b..3aae9b9 100644 --- a/test/cli/configuration.test.ts +++ b/test/cli/configuration.test.ts @@ -1,4 +1,4 @@ -import { assertEquals } from "../deps.ts" +import { assertEquals } from "@std/assert" import { generateConfiguration } from "../../lib/core/configuration.ts" Deno.test("Configuration - generateConfiguration creates a basic configuration", () => { diff --git a/test/cli/output.test.ts b/test/cli/output.test.ts index f2eb71e..02e8b6b 100644 --- a/test/cli/output.test.ts +++ b/test/cli/output.test.ts @@ -2,7 +2,7 @@ import { createFlagsMessage, createHeaderMessage, createUsageMessage } from "../../lib/cli/output.ts" import { Application } from "../../application.meta.ts" -import { assertEquals } from "../deps.ts" +import { assertEquals } from "@std/assert" Deno.test("Should correctly create the header message", () => { const expected = Application.name + " " + Application.version + "\n" + Application.repository diff --git a/test/common/eventemitter.test.ts b/test/common/eventemitter.test.ts index c3a550f..5686e9d 100644 --- a/test/common/eventemitter.test.ts +++ b/test/common/eventemitter.test.ts @@ -1,5 +1,5 @@ import { EventEmitter } from "../../lib/common/eventemitter.ts" -import { assert, assertEquals } from "../deps.ts" +import { assert, assertEquals } from "@std/assert" Deno.test("EventEmitter - Add and trigger event listener", () => { const eventEmitter = new EventEmitter() diff --git a/test/common/ipc.test.ts b/test/common/ipc.test.ts index 417e635..eb1287c 100644 --- a/test/common/ipc.test.ts +++ b/test/common/ipc.test.ts @@ -1,4 +1,4 @@ -import { assertEquals } from "../deps.ts" +import { assertEquals } from "@std/assert" import { FileIPC } from "../../lib/common/ipc.ts" import { fileExists } from "../../lib/common/utils.ts" diff --git a/test/common/utils.test.ts b/test/common/utils.test.ts index cb3dce0..9731989 100644 --- a/test/common/utils.test.ts +++ b/test/common/utils.test.ts @@ -1,5 +1,5 @@ import { dirExists, fileExists } from "../../lib/common/utils.ts" -import { assertEquals } from "../deps.ts" +import { assertEquals } from "@std/assert" Deno.test("dirExists - Directory exists", async () => { const tempDir = await Deno.makeTempDir() diff --git a/test/core/loadbalancer.test.ts b/test/core/loadbalancer.test.ts index fbc1cc1..cf006a4 100644 --- a/test/core/loadbalancer.test.ts +++ b/test/core/loadbalancer.test.ts @@ -1,5 +1,5 @@ // load_balancer_test.ts -import { assertEquals, assertThrows } from "../deps.ts" +import { assertEquals, assertThrows } from "@std/assert" import { Backend, BalancingStrategy, hashCode, LoadBalancer } from "../../lib/core/loadbalancer.ts" // Define logger callback function diff --git a/test/core/logger.test.ts b/test/core/logger.test.ts index dc42e43..79e2f01 100644 --- a/test/core/logger.test.ts +++ b/test/core/logger.test.ts @@ -1,4 +1,4 @@ -import { assertEquals, assertGreater } from "../deps.ts" +import { assertEquals, assertGreater } from "@std/assert" import { AttachedLogger, LogEventData, Logger } from "../../lib/core/logger.ts" import { ProcessConfiguration } from "../../mod.ts" diff --git a/test/core/plugin.test.ts b/test/core/plugin.test.ts index c3c78e5..4bc783d 100644 --- a/test/core/plugin.test.ts +++ b/test/core/plugin.test.ts @@ -1,4 +1,4 @@ -import { assertEquals, assertThrows } from "../deps.ts" +import { assertEquals, assertThrows } from "@std/assert" import { Pup } from "../../lib/core/pup.ts" import { Plugin, PluginApi } from "../../lib/core/plugin.ts" import { PluginConfiguration } from "../../lib/core/configuration.ts" diff --git a/test/core/pup.test.ts b/test/core/pup.test.ts index 9ea66dc..3618b0e 100644 --- a/test/core/pup.test.ts +++ b/test/core/pup.test.ts @@ -7,7 +7,7 @@ import { Configuration } from "../../lib/core/configuration.ts" import { ProcessState } from "../../lib/core/process.ts" import { Pup } from "../../lib/core/pup.ts" -import { assertEquals, assertNotEquals } from "../deps.ts" +import { assertEquals, assertNotEquals } from "@std/assert" Deno.test({ name: "Create test process. Test start, block, stop, start, unblock, start in sequence.", diff --git a/test/core/status.test.ts b/test/core/status.test.ts index 54881a5..c83c2ec 100644 --- a/test/core/status.test.ts +++ b/test/core/status.test.ts @@ -1,5 +1,5 @@ import { Status } from "../../lib/core/status.ts" -import { assertEquals } from "../deps.ts" +import { assertEquals } from "@std/assert" const TEST_FILE_PATH = "./test_data_Status.jsontest" diff --git a/test/deps.ts b/test/deps.ts deleted file mode 100644 index 71e80ab..0000000 --- a/test/deps.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { assert, assertEquals, assertGreater, assertNotEquals, assertRejects, assertThrows } from "https://deno.land/std@0.221.0/assert/mod.ts" -export { assertSpyCall, spy } from "https://deno.land/std@0.221.0/testing/mock.ts" diff --git a/test/main.test.ts b/test/main.test.ts index 70fb562..3fd7d9b 100644 --- a/test/main.test.ts +++ b/test/main.test.ts @@ -1,6 +1,6 @@ /* import { main } from "../lib/main.ts" -import { assertSpyCall, spy } from "./deps.ts" +import { assertSpyCall, spy } from "@std/testing/mock" Deno.test("main: exit with --version flag", async () => { const exitSpy = spy(Deno, "exit") diff --git a/test/telemetry.test.ts b/test/telemetry.test.ts index a05c2c2..26985e9 100644 --- a/test/telemetry.test.ts +++ b/test/telemetry.test.ts @@ -1,6 +1,6 @@ // deno-lint-ignore-file -import { assertEquals } from "./deps.ts" -import { PupTelemetry, TelemetryData } from "../telemetry.ts" +import { assertEquals } from "@std/assert" +import { PupTelemetry } from "../telemetry.ts" Deno.test("PupTelemetry - Singleton pattern", () => { const telemetry1 = new PupTelemetry() diff --git a/tools/build-schema.ts b/tools/build-schema.ts index 0f12aae..b193f81 100644 --- a/tools/build-schema.ts +++ b/tools/build-schema.ts @@ -1,4 +1,4 @@ -import { zodToJsonSchema } from "npm:zod-to-json-schema@3.21.4" +import { zodToJsonSchema } from "zod-to-json-schema" import { ConfigurationSchema } from "../lib/core/configuration.ts" const configurationSchema = ConfigurationSchema.describe("Pup configuration file") diff --git a/tools/release.ts b/tools/release.ts index 95c5fee..ab4d18e 100644 --- a/tools/release.ts +++ b/tools/release.ts @@ -4,7 +4,8 @@ * @file tools/release.ts * @license MIT */ -import { existsSync, parseVersion } from "../deps.ts" +import { parse } from "@std/semver" +import { exists } from "@cross/fs" import { Application } from "../application.meta.ts" const VERSIONS_JSON_PATH = "./versions.json" @@ -26,7 +27,7 @@ interface Versions { async function main() { let versions: Versions = { canary_url: "", stable: [], prerelease: [] } - if (existsSync(VERSIONS_JSON_PATH)) { + if (await exists(VERSIONS_JSON_PATH)) { const data = await Deno.readTextFile(VERSIONS_JSON_PATH) versions = JSON.parse(data) } @@ -42,10 +43,10 @@ async function main() { } // Parse the version using semver - const semver = parseVersion(Application.version) + const semver = parse(Application.version) // If semver.prerelease is not empty, it's a prerelease version - if (semver && semver.prerelease.length > 0) { + if (semver?.prerelease && semver.prerelease.length > 0) { versions.prerelease = versions.prerelease.filter((ver) => ver.version !== Application.version) versions.prerelease.unshift(newVersion) } else { From bc747c7048400878b4c39d362fc484f9a8d7cbcb Mon Sep 17 00:00:00 2001 From: Hexagon Date: Mon, 8 Apr 2024 01:11:42 +0200 Subject: [PATCH 03/13] Improvements --- .gitignore | 8 +++++++- deno.json | 2 +- lib/cli/args.ts | 6 +++--- test/cli/args.test.ts | 37 ++++++++++++++++++------------------- 4 files changed, 29 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index aa6e3e8..41c809f 100644 --- a/.gitignore +++ b/.gitignore @@ -47,5 +47,11 @@ core # Test coverage cov_profile *.lcov + # Lumocs generated site -_site \ No newline at end of file +_site + +# Node files +.npmrc +package-lock.json +package.json diff --git a/deno.json b/deno.json index 3830395..e7ec70b 100644 --- a/deno.json +++ b/deno.json @@ -22,7 +22,7 @@ "imports": { "@cross/fs": "jsr:@cross/fs@^0.0.8", "@cross/service": "jsr:@cross/service@^1.0.0", - "@cross/utils": "jsr:@cross/utils@^0.9.4", + "@cross/utils": "jsr:@cross/utils@^0.10.0", "@david/dax": "jsr:@david/dax@^0.40.0", "@hexagon/croner": "jsr:@hexagon/croner@^8.0.1", "@std/assert": "jsr:@std/assert@^0.221.0", diff --git a/lib/cli/args.ts b/lib/cli/args.ts index c44cb9a..9966c11 100644 --- a/lib/cli/args.ts +++ b/lib/cli/args.ts @@ -29,8 +29,8 @@ function parseArguments(args: string[]): ArgsParser { "upgrade": "update", "e": "env", } - - return new ArgsParser(args, { aliases }) + const boolean = ["setup", "upgrade", "help", "version", "autostart", "dry-run"] + return new ArgsParser(args, { aliases, boolean }) } /** @@ -87,7 +87,7 @@ function checkArguments(args: ArgsParser): ArgsParser { ] // Check that the base argument is either undefined or valid - const baseArgument = args.getRest() + const baseArgument = args.countLoose() > 0 ? args.getLoose()[0] : undefined; if (baseArgument !== undefined && (typeof baseArgument !== "string" || !validBaseArguments.includes(baseArgument))) { throw new Error(`Invalid base argument: ${baseArgument}`) } diff --git a/test/cli/args.test.ts b/test/cli/args.test.ts index 4eb81c0..7931e6f 100644 --- a/test/cli/args.test.ts +++ b/test/cli/args.test.ts @@ -1,10 +1,9 @@ -/* import { checkArguments, parseArguments } from "../../lib/cli/args.ts" import { assertEquals, assertThrows } from "@std/assert" import { spy } from "@std/testing/mock" import { Application } from "../../application.meta.ts" import { printHeader, printUsage } from "../../lib/cli/output.ts" -import { ArgsParser } from "jsr:@cross/utils@^0.8.0/args" +import { ArgsParser } from "@cross/utils" Deno.test("Boolean options and aliases are parsed correctly", () => { const inputArgs = [ @@ -19,15 +18,16 @@ Deno.test("Boolean options and aliases are parsed correctly", () => { "--cmd", ] const parsedArgs = parseArguments(inputArgs) - assertEquals(parsedArgs.getBoolean("version"), true); - assertEquals(parsedArgs.getBoolean("help"), true); - assertEquals(parsedArgs.getBoolean("autostart"), true); - assertEquals(parsedArgs.getLoose().includes("init"), true); - assertEquals(parsedArgs.getLoose().includes("append"), true); - assertEquals(parsedArgs.getLoose().includes("status"), true); - assertEquals(parsedArgs.getLoose().includes("remove"), true); - assertEquals(parsedArgs.getLoose().includes("run"), true); - assertEquals(parsedArgs.getLoose().includes("cmd"), true); + assertEquals(parsedArgs.getBoolean("version"), true) + assertEquals(parsedArgs.getBoolean("help"), true) + assertEquals(parsedArgs.getBoolean("autostart"), true) + console.log(parsedArgs.getLoose()) + assertEquals(parsedArgs.getLoose().includes("init"), true) + assertEquals(parsedArgs.getLoose().includes("append"), true) + assertEquals(parsedArgs.getLoose().includes("status"), true) + assertEquals(parsedArgs.getLoose().includes("remove"), true) + assertEquals(parsedArgs.getLoose().includes("run"), true) + assertEquals(parsedArgs.getBoolean("cmd"), true) }) Deno.test("String options and aliases are parsed correctly", () => { @@ -51,16 +51,15 @@ Deno.test("String options and aliases are parsed correctly", () => { "--dry-run", ] const parsedArgs = parseArguments(inputArgs) - assertEquals(parsedArgs.get("config"), "config.json"); - assertEquals(parsedArgs.get("watch"), "watched.json"); - assertEquals(parsedArgs.get("cmd"), "command"); - assertEquals(parsedArgs.getBoolean("dry-run"), true); - + assertEquals(parsedArgs.get("config"), "config.json") + assertEquals(parsedArgs.get("watch"), "watched.ts") + assertEquals(parsedArgs.get("cmd"), "command") + assertEquals(parsedArgs.getBoolean("dry-run"), true) }) - +/* Deno.test("checkArguments should throw error when autostart argument is provided without init, append or --cmd", async () => { const args = new ArgsParser(["--cron"]) - await assertThrows( + assertThrows( () => { checkArguments(args) }, @@ -70,7 +69,7 @@ Deno.test("checkArguments should throw error when autostart argument is provided }) Deno.test("checkArguments should throw error when cron argument is provided without init or append", async () => { - const args = { _: [], cron: true } + const args = new ArgsParser(["cron"]) assertThrows( () => { checkArguments(args) From 4bc95060eeb120e7d7411c69b7b4a4da66a86278 Mon Sep 17 00:00:00 2001 From: Hexagon Date: Thu, 11 Apr 2024 21:50:35 +0200 Subject: [PATCH 04/13] WIP --- README.md | 29 +++++++---- deno.json | 4 +- docs/src/changelog.md | 31 ++++++++++++ docs/src/faq.md | 6 +-- docs/src/index.md | 21 +++++--- lib/cli/args.ts | 12 ++--- lib/cli/config.ts | 17 ++++--- lib/cli/main.ts | 111 ++++++++++++++++++++++-------------------- lib/cli/output.ts | 47 ++++++++++-------- lib/common/ipc.ts | 11 +++-- lib/common/utils.ts | 66 +++++-------------------- lib/core/pup.ts | 5 +- lib/core/runner.ts | 2 +- 13 files changed, 192 insertions(+), 170 deletions(-) diff --git a/README.md b/README.md index 3d0fe06..cf8a8b3 100644 --- a/README.md +++ b/README.md @@ -25,33 +25,42 @@ _For detailed documentation, visit [pup.56k.guru](https://pup.56k.guru)._ ### Installation -To install Pup, make sure you have a fairly recent version of Deno installed (>=1.38.0), then open your terminal and execute the following command: +To install Pup, open your terminal and execute the following command: ```bash deno run -Ar https://deno.land/x/pup/pup.ts setup --channel prerelease ``` This command downloads the latest version of Pup and installs it on your system. The `--channel prerelease` option is included as there is no stable version of Pup yet. Read more abour release -channels [here](https://pup.56k.guru/installation.html#release-channels). +channels [here](https://hexagon.github.io/pup/installation.html#release-channels). + +To enable pup at system boot, install it as a system service using `pup enable-service`. + +```bash +pup enable-service +``` + +You can pass `-n my-custom-name` to give the service a name different from `pup` ### Configuration -1. Start by generating a new configuration file called pup.json at your project root. This can be achieved using Pup's built-in helper with the following command: +Pup revolves around ecosystem configuration files, each process belongs to an ecosystem defined by a `pup.json`. This file can either be created manually, or by the command line helpers. To create a +simple ecosystem running a single process: - `pup init --id "my-server" --cmd "deno run -A server.ts" --autostart` +`pup init --id "my-server" --cmd "deno run -A server.ts"` 2. (Optional) In case you have an additional task to execute, such as a cleanup script, you can make use of `pup append`. The following example shows how to add an extra task that use the cron start policy: `pup append --id "my-task" --cmd "deno run -A task.ts" --cron "0 0 * * * *"` -3. Now, start your ecosystem: +3. (Optional) Test run your instance (ecosystem): - `pup run` + `pup test` -4. (Optional) To make your ecosystem function as a system service, install it using `pup install`. This works with systemd, sysvinit, upstart, launchd, and Windows service manager: +4. To make your instance run at boot, enable it using `pup start`. - `pup install --name my-service` + `pup start` For the full manual, see @@ -59,9 +68,9 @@ For the full manual, see It is also possible to use pup to keep a process alive temporary, without a `pup.json` or system service. -To achieve this, use `pup run` with `--cmd` and a start policy, for example `--autostart`. +To achieve this, use `pup foreground` with `--cmd` and a start policy, the default restart policy is `--autostart`. -`pup run --cmd "deno run server.ts" --autostart` +`pup foreground --cmd "deno run server.ts"` ## Example setups diff --git a/deno.json b/deno.json index e7ec70b..59197f0 100644 --- a/deno.json +++ b/deno.json @@ -20,10 +20,9 @@ }, "imports": { - "@cross/fs": "jsr:@cross/fs@^0.0.8", + "@cross/fs": "jsr:@cross/fs@^0.0.9", "@cross/service": "jsr:@cross/service@^1.0.0", "@cross/utils": "jsr:@cross/utils@^0.10.0", - "@david/dax": "jsr:@david/dax@^0.40.0", "@hexagon/croner": "jsr:@hexagon/croner@^8.0.1", "@std/assert": "jsr:@std/assert@^0.221.0", "@std/async": "jsr:@std/async@^0.221.0", @@ -33,6 +32,7 @@ "@std/semver": "jsr:@std/semver@^0.221.0", "@std/testing": "jsr:@std/testing@^0.221.0", "@std/uuid": "jsr:@std/uuid@^0.221.0", + "dax-sh": "npm:dax-sh@^0.40.0", "zod": "npm:zod@^3.22.4", "zod-to-json-schema": "npm:zod-to-json-schema@^3.22.5" } diff --git a/docs/src/changelog.md b/docs/src/changelog.md index 02a8d1e..2f73202 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -9,6 +9,37 @@ nav_order: 13 All notable changes to this project will be documented in this section. +## [1.0.0-rc.15] - 2024-04-11 + +WIP, This is a roadmap. + +### Breaking Changes + +- [ ] change(cli): Rename cli command `run` to `foreground` +- [ ] change(cli): Rename cli command `install` to `service-enable` +- [ ] change(cli): Rename cli command `uninstall` to `service-disable` +- [ ] change(cli): Autostart by default if no other start policy is defined. +- [ ] change(core): Use a common process as system service and ecosystem manager. +- [ ] change(plugins): Remove built in plugins. +- [ ] change(api): Add HTTP API for building plugins, add-ons and utilities. + +### Non-breaking + +- [ ] fix(core): Foreground command did not keep an autostarted process running. +- [ ] change(docs): Rename instances to ecosystems everywhere. + +## Maintenance + +- [x] chore(deps): Replace `deno.land/x/udd` with `@check/deps` +- [x] chore(deps): Use `@cross/deps` for cross-runtime filesystem operations +- [x] chore(deps): Replace `deno.land/x/hexagon/service` with `@cross/service` for cross-runtime service installation +- [x] chore(deps): Replace `deno.land/x/std/` with `@std/` from jsr +- [x] chore(deps): Replace `deno.land/x/dax` with `dax-sh` for a cross runtime shell +- [x] chore(deps): Replace `deno.land/x/zod` with `npm:zod` +- [x] chore(deps): Utilize `@cross/utils` instead of Deno built-ins for cross runtime ansi console output, argument parsing, process management and more. +- [x] chore(deps): Use `@cross/env` to handle enviroment variables across runtimes. +- [ ] chore(testing): Use `@cross/test` insted of Deno test runner, to utilize the native test runners of Node, Deno and Bun. + ## [1.0.0-rc.14] - 2024-04-07 - fix(loadbalancer): Fixes an issue with the loadbalancer introduced in `1.0.0-rc.13` diff --git a/docs/src/faq.md b/docs/src/faq.md index 1ff253b..eee37e8 100644 --- a/docs/src/faq.md +++ b/docs/src/faq.md @@ -21,10 +21,10 @@ A: Yes, Pup is a language-agnostic process manager. You can use it to manage pro A: You can pass environment variables to your processes using the `env` property in the `ProcessConfiguration` object. This property accepts an object where keys represent the environment variable names and values represent their corresponding values. -**Q: Can I run multiple instances of Pup simultaneously?** +**Q: Can I run multiple ecosystems simultaneously?** -A: Yes, you can run multiple instances of Pup simultaneously. However, it is essential to ensure that each instance has a separate configuration file and that they do not conflict with each other in -terms of process IDs or shared resources. +A: Yes, you can run multiple ecosystems simultaneously. However, it is essential to ensure that each instance has a separate configuration file and that they do not conflict with each other in terms +of process IDs or shared resources. **Q: Is there a limit to the number of processes Pup can manage?** diff --git a/docs/src/index.md b/docs/src/index.md index a2e73e4..a0a0a28 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -36,21 +36,30 @@ deno run -Ar https://deno.land/x/pup/pup.ts setup --channel prerelease This command downloads the latest version of Pup and installs it on your system. The `--channel prerelease` option is included as there is no stable version of Pup yet. Read more abour release channels [here](https://hexagon.github.io/pup/installation.html#release-channels). +To enable pup at system boot, install it as a system service using `pup enable-service`. + +```bash +pup enable-service +``` + +You can pass `-n my-custom-name` to give the service a name different from `pup` + ### Configuration -1. Start by generating a new configuration file called pup.json at your project root. This can be achieved using Pup's built-in helper with the following command: +Pup revolves around ecosystem configuration files, each process belongs to an ecosystem defined by a `pup.json`. This file can either be created manually, or by the command line helpers. To create a +simple ecosystem running a single process: - `pup init --id "my-server" --cmd "deno run -A server.ts" --autostart` +`pup init --id "my-server" --cmd "deno run -A server.ts"` 2. (Optional) In case you have an additional task to execute, such as a cleanup script, you can make use of `pup append`. The following example shows how to add an extra task that use the cron start policy: `pup append --id "my-task" --cmd "deno run -A task.ts" --cron "0 0 * * * *"` -3. Now, start your ecosystem: +3. (Optional) Test run your instance (ecosystem): - `pup run` + `pup test` -4. (Optional) To make your ecosystem function as a system service, install it using `pup install`. This works with systemd, sysvinit, upstart, launchd, and Windows service manager: +4. To make your instance run at boot, enable it using `pup start`. - `pup install --name my-service` + `pup start` diff --git a/lib/cli/args.ts b/lib/cli/args.ts index 9966c11..13922c8 100644 --- a/lib/cli/args.ts +++ b/lib/cli/args.ts @@ -51,9 +51,9 @@ function checkArguments(args: ArgsParser): ArgsParser { "restart", "block", "unblock", - "run", - "install", - "uninstall", + "foreground", + "enable-service", + "disable-service", "logs", "upgrade", "update", @@ -87,14 +87,14 @@ function checkArguments(args: ArgsParser): ArgsParser { ] // Check that the base argument is either undefined or valid - const baseArgument = args.countLoose() > 0 ? args.getLoose()[0] : undefined; + const baseArgument = args.countLoose() > 0 ? args.getLoose()[0] : undefined if (baseArgument !== undefined && (typeof baseArgument !== "string" || !validBaseArguments.includes(baseArgument))) { throw new Error(`Invalid base argument: ${baseArgument}`) } const hasDoubleDashCmd = args.hasRest() const hasCmd = hasDoubleDashCmd || args.get("cmd") || args.get("worker") - const expectConfigOptions = baseArgument === "init" || baseArgument === "append" || (baseArgument === "run" && hasCmd) + const expectConfigOptions = baseArgument === "init" || baseArgument === "append" || (baseArgument === "foreground" && hasCmd) const expectInstallerOptions = baseArgument === "setup" || baseArgument === "upgrade" || baseArgument === "update" // Only one type of command can be present at the same time @@ -139,7 +139,7 @@ function checkArguments(args: ArgsParser): ArgsParser { } // --env flag can only be used with 'service install' base argument - if (args.get("env") && (baseArgument !== "install")) { + if (args.get("env") && (baseArgument !== "enable-service")) { throw new Error("Argument '--env' can only be used with 'service install' base argument") } diff --git a/lib/cli/config.ts b/lib/cli/config.ts index 3803f05..8e66157 100644 --- a/lib/cli/config.ts +++ b/lib/cli/config.ts @@ -8,9 +8,10 @@ import { Configuration, generateConfiguration, ProcessConfiguration } from "../core/configuration.ts" import * as jsonc from "@std/jsonc" -import { join, resolve } from "@std/path" -import { fileExists } from "../common/utils.ts" +import { join } from "@std/path" +import { exists } from "@cross/fs" import { ArgsParser } from "@cross/utils" +import { toResolvedAbsolutePath } from "../common/utils.ts" /** * Helper which creates a configuration file from command line arguments @@ -140,14 +141,14 @@ export async function removeFromConfigurationFile(configFile: string, checkedArg * * @async */ -export async function findConfigFile(useConfigFile?: boolean, argumentsConfigFile?: string, cwd?: string): Promise { +export async function findConfigFile(cwd: string, useConfigFile?: boolean, argumentsConfigFile?: string): Promise { // If not using config file, return undefined if (!useConfigFile) return undefined // If config file was specified in arguments, return it or throw if it does not exist if (argumentsConfigFile) { - const resolvedPath = resolve(argumentsConfigFile) - if (await fileExists(resolvedPath)) { + const resolvedPath = toResolvedAbsolutePath(argumentsConfigFile) + if (await exists(resolvedPath)) { return resolvedPath } else { throw new Error("Specified configuration file does not exist.") @@ -158,10 +159,10 @@ export async function findConfigFile(useConfigFile?: boolean, argumentsConfigFil let jsonPath = "./pup.json" let jsoncPath = "./pup.jsonc" if (cwd) { - jsonPath = join(resolve(cwd), jsonPath) - jsoncPath = join(resolve(cwd), jsoncPath) + jsonPath = join(toResolvedAbsolutePath(cwd), jsonPath) + jsoncPath = join(toResolvedAbsolutePath(cwd), jsoncPath) } - if (await fileExists(jsoncPath)) { + if (await exists(jsoncPath)) { return jsoncPath } else { return jsonPath diff --git a/lib/cli/main.ts b/lib/cli/main.ts index 98d0ed3..203fadb 100644 --- a/lib/cli/main.ts +++ b/lib/cli/main.ts @@ -18,7 +18,8 @@ import { getStatus, printStatus } from "./status.ts" import { upgrade } from "./upgrade.ts" // Import common utilities -import { fileExists, toPersistentPath, toTempPath } from "../common/utils.ts" +import { toPersistentPath, toResolvedAbsolutePath, toTempPath } from "../common/utils.ts" +import { exists, readFile } from "@cross/fs" // Import external dependencies import * as jsonc from "@std/jsonc" @@ -28,6 +29,8 @@ import { Logger } from "../core/logger.ts" import { args } from "@cross/utils/args" import { installService, uninstallService } from "@cross/service" +import { exit } from "@cross/utils" +import { chdir, cwd } from "@cross/fs" /** * Define the main entry point of the CLI application @@ -44,6 +47,8 @@ async function main() { /** * setup, upgrade + * + * setup is a special command used as the pup installer, to install pup as a system cli command */ const upgradeCondition = parsedArgs.get("setup") || baseArgument === "setup" const setupCondition = parsedArgs.get("upgrade") || baseArgument === "upgrade" || baseArgument === "update" @@ -58,10 +63,10 @@ async function main() { setupCondition as boolean, ) } catch (e) { - console.error(`Could not ${setupCondition ? "install" : "upgrade"} pup, error: ${e.message}`) + console.error(`Could not ${setupCondition ? "enable-service" : "upgrade"} pup, error: ${e.message}`) } // upgrader(...) will normally handle exiting with signal 0, so we exit with code 1 if getting here - Deno.exit(1) + exit(1) } /** @@ -69,7 +74,7 @@ async function main() { */ if (parsedArgs.get("version") !== undefined || baseArgument === "version") { printHeader() - Deno.exit(0) + exit(0) } /** @@ -79,7 +84,7 @@ async function main() { printUsage() console.log("") printFlags(parsedArgs.getBoolean("external-installer")) - Deno.exit(0) + exit(0) } /** @@ -90,7 +95,7 @@ async function main() { checkedArgs = checkArguments(parsedArgs) } catch (e) { console.error(`Invalid combination of arguments: ${e.message}`) - Deno.exit(1) + exit(1) } // Extract command from arguments @@ -104,7 +109,7 @@ async function main() { } // Extract worker from arguments - const worker = checkedArgs.get("worker") + const worker = checkedArgs?.get("worker") /** * Now either @@ -112,20 +117,21 @@ async function main() { * - Find configuration using (--config) * - Or generate configuration using (init) */ - const runWithoutConfig = baseArgument == "run" && (cmd !== undefined || worker !== undefined) + const runWithoutConfig = baseArgument == "foreground" && (cmd !== undefined || worker !== undefined) const useConfigFile = !runWithoutConfig let configFile if (useConfigFile) { - configFile = await findConfigFile(useConfigFile, checkedArgs.get("config"), checkedArgs.get("cwd")) + const configFileCwd = toResolvedAbsolutePath(checkedArgs?.get("cwd") || cwd()) + configFile = await findConfigFile(configFileCwd, useConfigFile, checkedArgs?.get("config")) } - + "" /** * Handle the install argument */ - if (baseArgument === "install") { + if (baseArgument === "enable-service") { if (!configFile) { console.error("Service maintenance commands require pup to run with a configuration file, exiting.") - Deno.exit(1) + exit(1) } const system = parsedArgs.getBoolean("system") @@ -139,12 +145,12 @@ async function main() { try { await installService({ system, name, cmd, cwd, user, home, env }, parsedArgs.getBoolean("dry-run")) - Deno.exit(0) + exit(0) } catch (e) { console.error(`Could not install service, error: ${e.message}`) - Deno.exit(1) + exit(1) } - } else if (baseArgument === "uninstall") { + } else if (baseArgument === "disable-service") { const system = parsedArgs.getBoolean("system") const name = parsedArgs.get("name") || "pup" const home = parsedArgs.get("home") @@ -152,10 +158,10 @@ async function main() { try { await uninstallService({ system, name, home }) console.log(`Service '${name}' uninstalled.`) - Deno.exit(0) + exit(0) } catch (e) { console.error(`Could not uninstall service, error: ${e.message}`) - Deno.exit(1) + exit(1) } } @@ -165,13 +171,13 @@ async function main() { if (baseArgument === "init") { // Default new configuration file to pup.jsonc const fallbackedConfigFile = configFile ?? "pup.jsonc" - if (await fileExists(fallbackedConfigFile)) { + if (await exists(fallbackedConfigFile)) { console.error(`Configuration file '${fallbackedConfigFile}' already exists, exiting.`) - Deno.exit(1) + exit(1) } else { - await createConfigurationFile(fallbackedConfigFile, checkedArgs, cmd!) + await createConfigurationFile(fallbackedConfigFile, checkedArgs!, cmd!) console.log(`Configuration file '${fallbackedConfigFile}' created`) - Deno.exit(0) + exit(0) } } @@ -182,30 +188,30 @@ async function main() { */ if (baseArgument === "append") { if (configFile) { - await appendConfigurationFile(configFile, checkedArgs, cmd!) + await appendConfigurationFile(configFile, checkedArgs!, cmd!) console.log(`Process '${parsedArgs.get("id")}' appended to configuration file '${configFile}'.`) - Deno.exit(0) + exit(0) } else { console.log(`Configuration file '${configFile}' not found, use init if you want to create a new one. Exiting.`) - Deno.exit(1) + exit(1) } } if (baseArgument === "remove") { if (configFile) { - await removeFromConfigurationFile(configFile, checkedArgs) + await removeFromConfigurationFile(configFile, checkedArgs!) console.log(`Process '${parsedArgs.get("id")}' removed from configuration file '${configFile}'.`) - Deno.exit(0) + exit(0) } else { console.log("Configuration file '${fallbackedConfigFile}' not found, use init if you want to create a new one. Exiting.") - Deno.exit(1) + exit(1) } } // Exit if no configuration file was found, or specified configuration file were not found if (useConfigFile && !configFile) { console.error("Configuration file not found.") - Deno.exit(1) + exit(1) } /** @@ -214,11 +220,11 @@ async function main() { if (useConfigFile && configFile) { try { const resolvedPath = path.parse(path.resolve(configFile)) - Deno.chdir(resolvedPath.dir) + chdir(resolvedPath.dir) configFile = `${resolvedPath.name}${resolvedPath.ext}` } catch (e) { console.error(`Could not change working directory to path of '${configFile}, exiting. Message: `, e.message) - Deno.exit(1) + exit(1) } } @@ -226,11 +232,12 @@ async function main() { let configuration: Configuration if (configFile) { try { - const rawConfig = await Deno.readTextFile(configFile) - configuration = validateConfiguration(jsonc.parse(rawConfig)) + const rawConfig = await readFile(configFile) + const rawConfigText = new TextDecoder().decode(rawConfig) + configuration = validateConfiguration(jsonc.parse(rawConfigText)) } catch (e) { console.error(`Could not start, error reading or parsing configuration file '${configFile}': ${e.message}`) - Deno.exit(1) + exit(1) } } else { configuration = generateConfiguration( @@ -251,7 +258,7 @@ async function main() { Deno.chdir(resolvedPath.dir) } catch (e) { console.error(`Could not change working directory to path specified by --cwd ${parsedArgs.get("cwd")}, exiting. Message: `, e.message) - Deno.exit(1) + exit(1) } } } @@ -301,7 +308,7 @@ async function main() { } else { console.error("No logs found.") } - Deno.exit(0) + exit(0) } // Prepare for IPC @@ -319,19 +326,19 @@ async function main() { if (baseArgument === "status") { if (!statusFile || !configFile) { console.error("Can not print status, no configuration file found") - Deno.exit(1) + exit(1) } console.log("") printHeader() await printStatus(configFile, statusFile) - Deno.exit(0) + exit(0) } // Handle --restart, --stop etc using IPC for (const op of ["restart", "start", "stop", "block", "unblock", "terminate"]) { if (baseArgument === op && !secondaryBaseArgument && baseArgument !== "terminate") { console.error(`Control functions require an id, specify with '${baseArgument} all|'`) - Deno.exit(1) + exit(1) } if (baseArgument === op) { // If status file doesn't exist, don't even try to communicate @@ -347,7 +354,7 @@ async function main() { const responseTimeout = setTimeout(() => { console.error("Response timeout after 10 seconds") - Deno.exit(1) + exit(1) }, 10000) // wait at most 10 seconds for await (const message of ipcResponse.receiveData()) { @@ -358,24 +365,24 @@ async function main() { console.log("Action completed successfully") } else { console.error("Action failed.") - Deno.exit(1) + exit(1) } } else { console.error("Action failed: Invalid response received.") - Deno.exit(1) + exit(1) } break // break out of the loop after receiving the response } - Deno.exit(0) + exit(0) } else { console.error(`No running instance found, cannot send command '${op}' over IPC.`) - Deno.exit(1) + exit(1) } } catch (e) { console.error(e.message) - Deno.exit(1) + exit(1) } } } @@ -383,18 +390,18 @@ async function main() { /** * handle the case where there is an existing status file */ - if (statusFile && await fileExists(statusFile)) { + if (statusFile && await exists(statusFile)) { try { // A valid status file were found if (!await getStatus(configFile, statusFile)) { /* ignore */ } else { console.warn(`An active status file were found at '${statusFile}', pup already running. Exiting.`) - Deno.exit(1) + exit(1) } } catch (e) { console.error(e.message) - Deno.exit(1) + exit(1) } } @@ -403,15 +410,15 @@ async function main() { */ if (!configuration || configuration?.processes?.length < 1) { console.error("No processes defined, exiting.") - Deno.exit(1) + exit(1) } /** * Ready to start pup! */ - if (baseArgument !== "run") { + if (baseArgument !== "foreground") { console.error("Trying to start pup without 'run' argument, this should not happen. Exiting.") - Deno.exit(1) + exit(1) } try { @@ -437,10 +444,10 @@ async function main() { await pup.terminate(30000) }) - // Let program end gracefully, no Deno.exit here + // Let program end gracefully, no exit here } catch (e) { console.error("Could not start pup, invalid configuration:", e.message) - Deno.exit(1) + exit(1) } } diff --git a/lib/cli/output.ts b/lib/cli/output.ts index fd3cd9c..630ced3 100644 --- a/lib/cli/output.ts +++ b/lib/cli/output.ts @@ -25,34 +25,27 @@ export function createFlagsMessage(externalInstaller: boolean): string { { short: "-h", long: "help", description: "Display this help and exit" }, { short: "-v", long: "version", description: "Output version information and exit" }, ) - if (!externalInstaller) { - rows.push( - { separator: "empty" }, - { content: "Install or upgrade pup", spanStart: 1 }, - { separator: "empty" }, - { long: "upgrade", description: "Upgrade pup and exit." }, - { long: "setup", description: "Install pup and exit." }, - { separator: "empty" }, - { long: "--channel ", description: "Select channel. stable (default), prerelease or canary." }, - { long: "--version ", description: "Install or upgrade to a specific version." }, - ) - } rows.push( { separator: "empty" }, - { content: "Start, control and monitor instances", spanStart: 1 }, + { content: "Control and monitor instances", spanStart: 1 }, { separator: "empty" }, - { long: "run", description: "Run a pup instance" }, + { long: "start", description: "Start and enable a pup instance" }, + { long: "stop", description: "Stop and disable a pup instance" }, + { long: "restart", description: "Restart a pup instance" }, + { long: "foreground", description: "Run a pup instance in foreground" }, { long: "terminate", description: "Terminate pup instance using IPC" }, - { long: "status", description: "Display status for a running instance" }, + { long: "status", description: "Show status for a pup instance" }, + { separator: "empty" }, + { short: "-c", long: "--config ", description: "Optional. Use specific configuration file." }, + { separator: "empty" }, + { content: "Control individual processes", spanStart: 1 }, { separator: "empty" }, - { long: "restart ", description: "Restart process using IPC" }, { long: "start ", description: "Start process using IPC" }, { long: "stop ", description: "Stop process using IPC" }, + { long: "restart ", description: "Restart process using IPC" }, { long: "block ", description: "Block process using IPC" }, { long: "unblock ", description: "Unblock process using IPC" }, { separator: "empty" }, - { short: "-c", long: "--config ", description: "Optional. Use specific configuration file." }, - { separator: "empty" }, { content: "Inspecting logs", spanStart: 1 }, { separator: "empty" }, { long: "logs", description: "View the logs for a running instance" }, @@ -69,8 +62,8 @@ export function createFlagsMessage(externalInstaller: boolean): string { { separator: "empty" }, { long: "init", description: "Initialize a new configuration file using the flags below." }, { long: "append", description: "Append a new process to the configuration file, " }, - { description: "configure using the flags below." }, { long: "remove", description: "Remove a process from a configuration file" }, + { description: "configure using the flags below." }, { separator: "empty" }, { short: "-c", long: "--config ", description: "Optional. Use specific configuration file." }, { description: "Default: ./pup.jsonc or ./pup.json" }, @@ -89,8 +82,8 @@ export function createFlagsMessage(externalInstaller: boolean): string { { separator: "empty" }, { content: "Service installation", spanStart: 1 }, { separator: "empty" }, - { long: "install", description: "Install pup instance as a service" }, - { long: "uninstall", description: "Uninstall service" }, + { long: "enable-service", description: "Install pup as a user service" }, + { long: "disable-service", description: "Uninstall pup service" }, { separator: "empty" }, { short: "-c", long: "--config ", description: "Optional. Use specific configuration file." }, { description: "Default: ./pup.jsonc or ./pup.json" }, @@ -105,6 +98,18 @@ export function createFlagsMessage(externalInstaller: boolean): string { { description: "e.g., --env KEY1=VALUE1 --env KEY2=VALUE2." }, { separator: "empty" }, ) + if (!externalInstaller) { + rows.push( + { separator: "empty" }, + { content: "Upgrade pup", spanStart: 1 }, + { separator: "empty" }, + { long: "upgrade", description: "Upgrade pup and exit." }, + /* { long: "setup", description: "Install pup and exit." }, Keep setup undocoumented to avoid confusion */ + { separator: "empty" }, + { long: "--channel ", description: "Select channel. stable (default), prerelease or canary." }, + { long: "--version ", description: "Install or upgrade to a specific version." }, + ) + } const columns: Column[] = [ { key: "short", align: "right", minWidth: 8 }, diff --git a/lib/common/ipc.ts b/lib/common/ipc.ts index 0556e68..f93ebaf 100644 --- a/lib/common/ipc.ts +++ b/lib/common/ipc.ts @@ -9,9 +9,10 @@ * @license MIT */ -import { fileExists } from "../common/utils.ts" -import { basename, dirname, join, resolve } from "@std/path" +import { exists } from "@cross/fs" +import { basename, dirname, join } from "@std/path" import { debounce } from "@std/async" +import { toResolvedAbsolutePath } from "./utils.ts" export interface IpcValidatedMessage { pid: number | null @@ -32,8 +33,8 @@ export class FileIPC { private watcher?: Deno.FsWatcher constructor(filePath: string, staleMessageLimitMs?: number, debounceTimeMs?: number) { - this.filePath = resolve(filePath) - this.dirPath = resolve(dirname(filePath)) // Get directory of the file + this.filePath = toResolvedAbsolutePath(filePath) + this.dirPath = toResolvedAbsolutePath(dirname(filePath)) // Get directory of the file this.fileName = basename(filePath) // Get name of the file this.staleMessageLimitMs = staleMessageLimitMs ?? 30000 this.debounceTimeMs = debounceTimeMs ?? 100 @@ -92,7 +93,7 @@ export class FileIPC { * Note: This function should only be used internally by the FileIPC class and is not meant to be exposed to external consumers. */ private async extractMessages(): Promise { - if (await fileExists(this.filePath)) { + if (await exists(this.filePath)) { let fileContent try { fileContent = await Deno.readTextFile(this.filePath) diff --git a/lib/common/utils.ts b/lib/common/utils.ts index 67d1ea7..cd29106 100644 --- a/lib/common/utils.ts +++ b/lib/common/utils.ts @@ -5,55 +5,15 @@ * @license MIT */ -import { parse, resolve } from "@std/path" +import { isAbsolute, parse, resolve } from "@std/path" +import { cwd, mkdir } from "@cross/fs" -/** - * Check if a file exists. - * @async - * @function - * @param {string} filePath - The path to the file to check. - * @returns {Promise} A promise that resolves to true if the file exists, false otherwise. - * @throws {Error} Throws an error if an error occurs other than the file not being found. - */ -export async function fileExists(filePath: string): Promise { - try { - const statResult = await Deno.stat(filePath) - if (statResult.isFile) { - return true - } else { - return false - } - } catch (e) { - if (e instanceof Deno.errors.NotFound) { - return false - } else { - throw e - } - } -} - -/** - * Check if a directory exists. - * @async - * @function - * @param {string} dirPath - The path to the directory to check. - * @returns {Promise} A promise that resolves to true if the directory exists, false otherwise. - * @throws {Error} Throws an error if an error occurs other than the directory not being found. - */ -export async function dirExists(dirPath: string): Promise { - try { - const statResult = await Deno.stat(dirPath) - if (statResult.isDirectory) { - return true - } else { - return false - } - } catch (e) { - if (e instanceof Deno.errors.NotFound) { - return false - } else { - throw e - } +export function toResolvedAbsolutePath(path: string, cwdInput?: string) { + const cwdToUse = cwdInput || cwd() + if (!isAbsolute(path)) { + return resolve(cwdToUse, path) + } else { + return resolve(path) } } @@ -64,9 +24,9 @@ export async function dirExists(dirPath: string): Promise { * @returns {string} The temporary path associated with the configuration file. */ export async function toTempPath(configFile: string) { - const resolvedPath = parse(resolve(configFile)) - const tempPath = resolve(`${resolvedPath.dir}/.${resolvedPath.name}${resolvedPath.ext}-tmp`) - await Deno.mkdir(tempPath, { recursive: true }) + const resolvedPath = parse(toResolvedAbsolutePath(configFile)) + const tempPath = toResolvedAbsolutePath(`${resolvedPath.dir}/.${resolvedPath.name}${resolvedPath.ext}-tmp`) + await mkdir(tempPath, { recursive: true }) return tempPath } @@ -77,8 +37,8 @@ export async function toTempPath(configFile: string) { * @returns {string} The persistent storage path associated with the configuration file. */ export async function toPersistentPath(configFile: string) { - const resolvedPath = parse(resolve(configFile)) + const resolvedPath = parse(toResolvedAbsolutePath(configFile)) const persistentStoragePath = resolve(`${resolvedPath.dir}/.${resolvedPath.name}${resolvedPath.ext}-data`) - await Deno.mkdir(persistentStoragePath, { recursive: true }) + await mkdir(persistentStoragePath, { recursive: true }) return persistentStoragePath } diff --git a/lib/core/pup.ts b/lib/core/pup.ts index 2f95b50..495798e 100644 --- a/lib/core/pup.ts +++ b/lib/core/pup.ts @@ -12,9 +12,8 @@ import { Process, ProcessState } from "./process.ts" import { Status } from "./status.ts" import { Plugin } from "./plugin.ts" import { Cluster } from "./cluster.ts" -import * as path from "@std/path" import { EventEmitter } from "../common/eventemitter.ts" -import { toPersistentPath, toTempPath } from "../common/utils.ts" +import { toPersistentPath, toResolvedAbsolutePath, toTempPath } from "../common/utils.ts" import * as uuid from "@std/uuid" interface InstructionResponse { @@ -57,7 +56,7 @@ class Pup { let ipcFile let logStore if (configFilePath && temporaryStoragePath && persistentStoragePath) { - this.configFilePath = path.resolve(configFilePath) + this.configFilePath = toResolvedAbsolutePath(configFilePath) this.temporaryStoragePath = temporaryStoragePath this.cleanupQueue.push(this.temporaryStoragePath) diff --git a/lib/core/runner.ts b/lib/core/runner.ts index 4a8674c..85f4986 100644 --- a/lib/core/runner.ts +++ b/lib/core/runner.ts @@ -8,7 +8,7 @@ import { ProcessConfiguration, Pup } from "./pup.ts" import { readLines, StringReader } from "@std/io" import { BaseRunner, RunnerCallback, RunnerResult } from "../types/runner.ts" -import { $, CommandChild } from "@david/dax" +import { $, CommandChild } from "dax-sh" /** * Represents a task runner that executes tasks as regular processes. * Extends the BaseRunner class. From 4f74c85e7426b9f28c1ae8a575e1a66631c35c17 Mon Sep 17 00:00:00 2001 From: Hexagon Date: Thu, 11 Apr 2024 21:54:43 +0200 Subject: [PATCH 05/13] Update changelog --- docs/src/changelog.md | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/docs/src/changelog.md b/docs/src/changelog.md index 2f73202..ffd49c8 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -11,33 +11,37 @@ All notable changes to this project will be documented in this section. ## [1.0.0-rc.15] - 2024-04-11 -WIP, This is a roadmap. - ### Breaking Changes -- [ ] change(cli): Rename cli command `run` to `foreground` -- [ ] change(cli): Rename cli command `install` to `service-enable` -- [ ] change(cli): Rename cli command `uninstall` to `service-disable` +- [X] change(cli): Rename cli command `run` to `foreground` +- [X] change(cli): Rename cli command `install` to `service-enable` +- [X] change(cli): Rename cli command `uninstall` to `service-disable` - [ ] change(cli): Autostart by default if no other start policy is defined. - [ ] change(core): Use a common process as system service and ecosystem manager. -- [ ] change(plugins): Remove built in plugins. +- [ ] change(core): Introduce `$HOME/.pup/global.json` tracking global settings and enabled ecosystems. - [ ] change(api): Add HTTP API for building plugins, add-ons and utilities. +- [ ] change(plugins): Break out built in plugins to separate packages. +- [ ] change(logging): Make logs streaming by default. +- [ ] change(config): Support JSON5. +- [ ] change(status): Make status show all ecosystems by default, or a single ecosystem if a `pup.json` is in within the path, or specified using `-c`. +- [ ] change(packaging): Use jsr.io instead of deno.land/x for distribution. +- [ ] change(packaging]: Specify installation command for next version in version metadata instead of code, allowing for new installation commands for new versions. ### Non-breaking -- [ ] fix(core): Foreground command did not keep an autostarted process running. +- [ ] fix(core): Foreground command does not keep an autostarted process running. - [ ] change(docs): Rename instances to ecosystems everywhere. ## Maintenance -- [x] chore(deps): Replace `deno.land/x/udd` with `@check/deps` -- [x] chore(deps): Use `@cross/deps` for cross-runtime filesystem operations -- [x] chore(deps): Replace `deno.land/x/hexagon/service` with `@cross/service` for cross-runtime service installation -- [x] chore(deps): Replace `deno.land/x/std/` with `@std/` from jsr -- [x] chore(deps): Replace `deno.land/x/dax` with `dax-sh` for a cross runtime shell -- [x] chore(deps): Replace `deno.land/x/zod` with `npm:zod` -- [x] chore(deps): Utilize `@cross/utils` instead of Deno built-ins for cross runtime ansi console output, argument parsing, process management and more. -- [x] chore(deps): Use `@cross/env` to handle enviroment variables across runtimes. +- [X] chore(deps): Replace `deno.land/x/udd` with `@check/deps` +- [X] chore(deps): Use `@cross/deps` for cross-runtime filesystem operations +- [X] chore(deps): Replace `deno.land/x/hexagon/service` with `@cross/service` for cross-runtime service installation +- [X] chore(deps): Replace `deno.land/x/std/` with `@std/` from jsr +- [X] chore(deps): Replace `deno.land/x/dax` with `dax-sh` for a cross runtime shell +- [X] chore(deps): Replace `deno.land/x/zod` with `npm:zod` +- [X] chore(deps): Utilize `@cross/utils` instead of Deno built-ins for cross runtime ansi console output, argument parsing, process management and more. +- [X] chore(deps): Use `@cross/env` to handle enviroment variables across runtimes. - [ ] chore(testing): Use `@cross/test` insted of Deno test runner, to utilize the native test runners of Node, Deno and Bun. ## [1.0.0-rc.14] - 2024-04-07 From b160fa3343e070b6a5ff428350ae8eded19a6af2 Mon Sep 17 00:00:00 2001 From: Hexagon Date: Fri, 12 Apr 2024 21:25:58 +0200 Subject: [PATCH 06/13] Changes --- README.md | 22 ++++++++-------------- docs/src/changelog.md | 29 +++++++++++------------------ docs/src/faq.md | 6 +++--- docs/src/index.md | 22 ++++++++-------------- lib/cli/output.ts | 39 ++++++++++++++++----------------------- 5 files changed, 46 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index cf8a8b3..8a15b7b 100644 --- a/README.md +++ b/README.md @@ -34,20 +34,12 @@ deno run -Ar https://deno.land/x/pup/pup.ts setup --channel prerelease This command downloads the latest version of Pup and installs it on your system. The `--channel prerelease` option is included as there is no stable version of Pup yet. Read more abour release channels [here](https://hexagon.github.io/pup/installation.html#release-channels). -To enable pup at system boot, install it as a system service using `pup enable-service`. - -```bash -pup enable-service -``` - -You can pass `-n my-custom-name` to give the service a name different from `pup` - ### Configuration -Pup revolves around ecosystem configuration files, each process belongs to an ecosystem defined by a `pup.json`. This file can either be created manually, or by the command line helpers. To create a -simple ecosystem running a single process: +Pup revolves around instance (ecosystem) configuration files, each process belongs to an instance defined by a `pup.json`. This file can either be created manually, or by the command line helpers. To +create a simple instance running a single process: -`pup init --id "my-server" --cmd "deno run -A server.ts"` +`pup init --id "my-server" --autostart --cmd "deno run -A server.ts"` 2. (Optional) In case you have an additional task to execute, such as a cleanup script, you can make use of `pup append`. The following example shows how to add an extra task that use the cron start policy: @@ -56,11 +48,13 @@ simple ecosystem running a single process: 3. (Optional) Test run your instance (ecosystem): - `pup test` + `pup foreground` + +4. To make your instance run at boot, enable it using `pup enable-service`. -4. To make your instance run at boot, enable it using `pup start`. + `pup enable-service` - `pup start` + You can pass `-n my-custom-name` to give the service a name different from `pup` For the full manual, see diff --git a/docs/src/changelog.md b/docs/src/changelog.md index ffd49c8..825b1b0 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -13,35 +13,28 @@ All notable changes to this project will be documented in this section. ### Breaking Changes -- [X] change(cli): Rename cli command `run` to `foreground` -- [X] change(cli): Rename cli command `install` to `service-enable` -- [X] change(cli): Rename cli command `uninstall` to `service-disable` -- [ ] change(cli): Autostart by default if no other start policy is defined. -- [ ] change(core): Use a common process as system service and ecosystem manager. -- [ ] change(core): Introduce `$HOME/.pup/global.json` tracking global settings and enabled ecosystems. -- [ ] change(api): Add HTTP API for building plugins, add-ons and utilities. -- [ ] change(plugins): Break out built in plugins to separate packages. +- [x] change(cli): Rename cli command `run` to `foreground` +- [x] change(cli): Rename cli command `install` to `enable-service` +- [x] change(cli): Rename cli command `uninstall` to `disable-service` - [ ] change(logging): Make logs streaming by default. - [ ] change(config): Support JSON5. -- [ ] change(status): Make status show all ecosystems by default, or a single ecosystem if a `pup.json` is in within the path, or specified using `-c`. - [ ] change(packaging): Use jsr.io instead of deno.land/x for distribution. - [ ] change(packaging]: Specify installation command for next version in version metadata instead of code, allowing for new installation commands for new versions. ### Non-breaking - [ ] fix(core): Foreground command does not keep an autostarted process running. -- [ ] change(docs): Rename instances to ecosystems everywhere. ## Maintenance -- [X] chore(deps): Replace `deno.land/x/udd` with `@check/deps` -- [X] chore(deps): Use `@cross/deps` for cross-runtime filesystem operations -- [X] chore(deps): Replace `deno.land/x/hexagon/service` with `@cross/service` for cross-runtime service installation -- [X] chore(deps): Replace `deno.land/x/std/` with `@std/` from jsr -- [X] chore(deps): Replace `deno.land/x/dax` with `dax-sh` for a cross runtime shell -- [X] chore(deps): Replace `deno.land/x/zod` with `npm:zod` -- [X] chore(deps): Utilize `@cross/utils` instead of Deno built-ins for cross runtime ansi console output, argument parsing, process management and more. -- [X] chore(deps): Use `@cross/env` to handle enviroment variables across runtimes. +- [x] chore(deps): Replace `deno.land/x/udd` with `@check/deps` +- [x] chore(deps): Use `@cross/deps` for cross-runtime filesystem operations +- [x] chore(deps): Replace `deno.land/x/hexagon/service` with `@cross/service` for cross-runtime service installation +- [x] chore(deps): Replace `deno.land/x/std/` with `@std/` from jsr +- [x] chore(deps): Replace `deno.land/x/dax` with `dax-sh` for a cross runtime shell +- [x] chore(deps): Replace `deno.land/x/zod` with `npm:zod` +- [x] chore(deps): Utilize `@cross/utils` instead of Deno built-ins for cross runtime ansi console output, argument parsing, process management and more. +- [x] chore(deps): Use `@cross/env` to handle enviroment variables across runtimes. - [ ] chore(testing): Use `@cross/test` insted of Deno test runner, to utilize the native test runners of Node, Deno and Bun. ## [1.0.0-rc.14] - 2024-04-07 diff --git a/docs/src/faq.md b/docs/src/faq.md index eee37e8..2a590bd 100644 --- a/docs/src/faq.md +++ b/docs/src/faq.md @@ -21,10 +21,10 @@ A: Yes, Pup is a language-agnostic process manager. You can use it to manage pro A: You can pass environment variables to your processes using the `env` property in the `ProcessConfiguration` object. This property accepts an object where keys represent the environment variable names and values represent their corresponding values. -**Q: Can I run multiple ecosystems simultaneously?** +**Q: Can I run multiple instances simultaneously?** -A: Yes, you can run multiple ecosystems simultaneously. However, it is essential to ensure that each instance has a separate configuration file and that they do not conflict with each other in terms -of process IDs or shared resources. +A: Yes, you can run multiple instances simultaneously. However, it is essential to ensure that each instance has a separate configuration file and that they do not conflict with each other in terms of +process IDs or shared resources. **Q: Is there a limit to the number of processes Pup can manage?** diff --git a/docs/src/index.md b/docs/src/index.md index a0a0a28..39abcef 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -36,20 +36,12 @@ deno run -Ar https://deno.land/x/pup/pup.ts setup --channel prerelease This command downloads the latest version of Pup and installs it on your system. The `--channel prerelease` option is included as there is no stable version of Pup yet. Read more abour release channels [here](https://hexagon.github.io/pup/installation.html#release-channels). -To enable pup at system boot, install it as a system service using `pup enable-service`. - -```bash -pup enable-service -``` - -You can pass `-n my-custom-name` to give the service a name different from `pup` - ### Configuration -Pup revolves around ecosystem configuration files, each process belongs to an ecosystem defined by a `pup.json`. This file can either be created manually, or by the command line helpers. To create a -simple ecosystem running a single process: +Pup revolves around instances configuration files, each process belongs to an instances defined by a `pup.json`. This file can either be created manually, or by the command line helpers. To create a +simple instances running a single process: -`pup init --id "my-server" --cmd "deno run -A server.ts"` +`pup init --id "my-server" --autostart --cmd "deno run -A server.ts"` 2. (Optional) In case you have an additional task to execute, such as a cleanup script, you can make use of `pup append`. The following example shows how to add an extra task that use the cron start policy: @@ -58,8 +50,10 @@ simple ecosystem running a single process: 3. (Optional) Test run your instance (ecosystem): - `pup test` + `pup foreground` + +4. To make your instance run at boot, enable it using `pup enable-service`. -4. To make your instance run at boot, enable it using `pup start`. + `pup enable-service` - `pup start` + You can pass `-n my-custom-name` to give the service a name different from `pup` diff --git a/lib/cli/output.ts b/lib/cli/output.ts index 630ced3..c38605d 100644 --- a/lib/cli/output.ts +++ b/lib/cli/output.ts @@ -29,23 +29,34 @@ export function createFlagsMessage(externalInstaller: boolean): string { { separator: "empty" }, { content: "Control and monitor instances", spanStart: 1 }, { separator: "empty" }, - { long: "start", description: "Start and enable a pup instance" }, - { long: "stop", description: "Stop and disable a pup instance" }, - { long: "restart", description: "Restart a pup instance" }, { long: "foreground", description: "Run a pup instance in foreground" }, { long: "terminate", description: "Terminate pup instance using IPC" }, { long: "status", description: "Show status for a pup instance" }, { separator: "empty" }, { short: "-c", long: "--config ", description: "Optional. Use specific configuration file." }, { separator: "empty" }, - { content: "Control individual processes", spanStart: 1 }, - { separator: "empty" }, { long: "start ", description: "Start process using IPC" }, { long: "stop ", description: "Stop process using IPC" }, { long: "restart ", description: "Restart process using IPC" }, { long: "block ", description: "Block process using IPC" }, { long: "unblock ", description: "Unblock process using IPC" }, { separator: "empty" }, + { content: "Service installation", spanStart: 1 }, + { separator: "empty" }, + { long: "enable-service", description: "Start pup instance at boot" }, + { long: "disable-service", description: "Uninstall pup service" }, + { separator: "empty" }, + { short: "-c", long: "--config ", description: "Optional. Use specific configuration file." }, + { description: "Default: ./pup.jsonc or ./pup.json" }, + { long: "--dry-run", description: "Generate and output service configuration, do not actually install the service" }, + { long: "--system", description: "Optional. Install the service system-wide (requires root)." }, + { long: "--cwd", description: "Optional. Set working directory for service" }, + { long: "--name", description: "Optional. Set service name" }, + { long: "--user", description: "Optional. Set service run-as user" }, + { long: "--home", description: "Optional. Set service home directory" }, + { long: "--env", short: "-e", description: "Optional. Set environment variables for service, in the format KEY=VALUE." }, + { description: "Multiple variables can be passed by using the flag multiple times," }, + { description: "e.g., --env KEY1=VALUE1 --env KEY2=VALUE2." }, { content: "Inspecting logs", spanStart: 1 }, { separator: "empty" }, { long: "logs", description: "View the logs for a running instance" }, @@ -79,24 +90,6 @@ export function createFlagsMessage(externalInstaller: boolean): string { { long: "--start-port", description: "Cluster: Start port for instances." }, { long: "--common-port", description: "Cluster: Common port for instances." }, { long: "--strategy", description: "Cluster: Load balancing strategy (defaults to round-robin)." }, - { separator: "empty" }, - { content: "Service installation", spanStart: 1 }, - { separator: "empty" }, - { long: "enable-service", description: "Install pup as a user service" }, - { long: "disable-service", description: "Uninstall pup service" }, - { separator: "empty" }, - { short: "-c", long: "--config ", description: "Optional. Use specific configuration file." }, - { description: "Default: ./pup.jsonc or ./pup.json" }, - { long: "--dry-run", description: "Generate and output service configuration, do not actually install the service" }, - { long: "--system", description: "Optional. Install the service system-wide (requires root)." }, - { long: "--cwd", description: "Optional. Set working directory for service" }, - { long: "--name", description: "Optional. Set service name" }, - { long: "--user", description: "Optional. Set service run-as user" }, - { long: "--home", description: "Optional. Set service home directory" }, - { long: "--env", short: "-e", description: "Optional. Set environment variables for service, in the format KEY=VALUE." }, - { description: "Multiple variables can be passed by using the flag multiple times," }, - { description: "e.g., --env KEY1=VALUE1 --env KEY2=VALUE2." }, - { separator: "empty" }, ) if (!externalInstaller) { rows.push( From d60b93c92cecbb262f40854535ee6eafb62ed82c Mon Sep 17 00:00:00 2001 From: Hexagon Date: Sat, 13 Apr 2024 00:46:29 +0200 Subject: [PATCH 07/13] Work in progress --- .github/workflows/deno.yaml | 2 +- application.meta.ts | 2 +- deno.json | 2 +- docs/src/changelog.md | 7 +- .../src/examples/basic-webinterface/README.md | 4 +- docs/src/examples/basic/README.md | 2 +- docs/src/examples/cluster/README.md | 2 +- docs/src/examples/splunk/README.md | 4 +- docs/src/examples/telemetry/README.md | 2 +- docs/src/examples/watcher/README.md | 4 +- docs/src/examples/worker/README.md | 2 +- docs/src/index.md | 4 +- docs/src/installation.md | 4 +- docs/src/usage/configuration.md | 10 +-- docs/src/usage/service.md | 16 ++-- lib/cli/config.ts | 16 ++-- lib/cli/main.ts | 38 ++++++--- lib/cli/output.ts | 6 +- lib/common/utils.ts | 4 +- lib/core/runner.ts | 4 +- telemetry.ts | 8 +- test/cli/args.test.ts | 3 +- test/common/ipc.test.ts | 12 +-- test/common/utils.test.ts | 78 ------------------- 24 files changed, 90 insertions(+), 146 deletions(-) delete mode 100644 test/common/utils.test.ts diff --git a/.github/workflows/deno.yaml b/.github/workflows/deno.yaml index efc0e30..1047c54 100644 --- a/.github/workflows/deno.yaml +++ b/.github/workflows/deno.yaml @@ -16,7 +16,7 @@ jobs: steps: - name: Git Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Use Deno Version ${{ matrix.deno-version }} uses: denoland/setup-deno@v1 diff --git a/application.meta.ts b/application.meta.ts index abc992a..c529f97 100644 --- a/application.meta.ts +++ b/application.meta.ts @@ -21,7 +21,7 @@ const Application = { name: "pup", - version: "1.0.0-rc.14", + version: "1.0.0-rc.15", url: "https://deno.land/x/pup@$VERSION/pup.ts", canary_url: "https://raw.githubusercontent.com/Hexagon/pup/main/pup.ts", deno: null, /* Minimum stable version of Deno required to run Pup (without --unstable-* flags) */ diff --git a/deno.json b/deno.json index 59197f0..45c4f29 100644 --- a/deno.json +++ b/deno.json @@ -27,12 +27,12 @@ "@std/assert": "jsr:@std/assert@^0.221.0", "@std/async": "jsr:@std/async@^0.221.0", "@std/io": "jsr:@std/io@^0.221.0", - "@std/jsonc": "jsr:@std/jsonc@^0.221.0", "@std/path": "jsr:@std/path@^0.221.0", "@std/semver": "jsr:@std/semver@^0.221.0", "@std/testing": "jsr:@std/testing@^0.221.0", "@std/uuid": "jsr:@std/uuid@^0.221.0", "dax-sh": "npm:dax-sh@^0.40.0", + "json5": "npm:json5@^2.2.3", "zod": "npm:zod@^3.22.4", "zod-to-json-schema": "npm:zod-to-json-schema@^3.22.5" } diff --git a/docs/src/changelog.md b/docs/src/changelog.md index 825b1b0..9548437 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -17,13 +17,14 @@ All notable changes to this project will be documented in this section. - [x] change(cli): Rename cli command `install` to `enable-service` - [x] change(cli): Rename cli command `uninstall` to `disable-service` - [ ] change(logging): Make logs streaming by default. -- [ ] change(config): Support JSON5. -- [ ] change(packaging): Use jsr.io instead of deno.land/x for distribution. -- [ ] change(packaging]: Specify installation command for next version in version metadata instead of code, allowing for new installation commands for new versions. +- [x] change(config): Support JSON5. ### Non-breaking - [ ] fix(core): Foreground command does not keep an autostarted process running. +- [x] fix(cli): Controlled output of manual rests after installing/uninstalling as service. +- [x] fix(docs): Docs incorrectly stated that `**/_._` is default for watcher config. `**/*.*` is correct. +- [ ] chore(docs): Add a PM2 migration guide. ## Maintenance diff --git a/docs/src/examples/basic-webinterface/README.md b/docs/src/examples/basic-webinterface/README.md index feddc7a..fe2be81 100644 --- a/docs/src/examples/basic-webinterface/README.md +++ b/docs/src/examples/basic-webinterface/README.md @@ -11,7 +11,7 @@ nav_order: 3 The example at [/docs/src/examples/basic-webinterface](https://github.com/Hexagon/pup/tree/main/docs/src/examples/basic-webinterface) borrows the server.ts and task.ts from the basic example. Both processes have logging configurations, with the second process having custom logger settings which enable the periodic process to write its logs to separate files. -The web interface plugin is enabled by `pup.jsonc` and available at +The web interface plugin is enabled by `pup.json` and available at ## Example Files @@ -43,7 +43,7 @@ intallation, you **can not** use the `$VERSION` variable, and should give an abs `cd` to `/docs/src/examples/basic-webinterface` directory. -Start example by running `pup run` if pup is installed, or something like `deno run -A ../../../pup.ts run` if not. +Start example by running `pup foreground` if pup is installed, or something like `deno run -A ../../../pup.ts foreground` if not. Browse to diff --git a/docs/src/examples/basic/README.md b/docs/src/examples/basic/README.md index 3a64965..405cdf3 100644 --- a/docs/src/examples/basic/README.md +++ b/docs/src/examples/basic/README.md @@ -25,6 +25,6 @@ its logs to separate files. `cd` to `/docs/src/examples/basic` directory. -Start example by running `pup run` if pup is installed, or something like `deno run -A ../../../pup.ts run` if not. +Start example by running `pup foreground` if pup is installed, or something like `deno run -A ../../../pup.ts foreground` if not. Success! diff --git a/docs/src/examples/cluster/README.md b/docs/src/examples/cluster/README.md index ed65774..91204c5 100644 --- a/docs/src/examples/cluster/README.md +++ b/docs/src/examples/cluster/README.md @@ -22,7 +22,7 @@ Detailed documentation available at [5. Scaling applications](https://hexagon.gi `cd` to `/docs/src/examples/cluster` directory. -Run using command `pup run` +Run using command `pup foreground` Browse to `http://localhost:3456` diff --git a/docs/src/examples/splunk/README.md b/docs/src/examples/splunk/README.md index 7eb49ee..d22824f 100644 --- a/docs/src/examples/splunk/README.md +++ b/docs/src/examples/splunk/README.md @@ -12,7 +12,7 @@ The example at [/docs/src/examples/splunk](https://github.com/Hexagon/pup/tree/m output to Splunk using the splunk-hec plugin. > **Note:** If you're connecting to a Splunk HEC server with a bad certificate, such as during testing, you'll need to start pup manually with the `--unsafely-ignore-certificate-errors` flag. The full -> command for this would be `deno run -Ar --unsafely-ignore-certificate-errors https://deno.land/x/pup/pup.ts run` { .note } +> command for this would be `deno run -Ar --unsafely-ignore-certificate-errors https://deno.land/x/pup/pup.ts foreground` { .note } ## Files @@ -29,6 +29,6 @@ The HEC token can be configured in the `pup.jsonc` configuration file as shown i `cd` to `/docs/src/examples/splunk` directory. -Start the example by running `pup run` if pup is installed, or something like `deno run -A ../../../pup.ts run` if not. +Start the example by running `pup foreground` if pup is installed, or something like `deno run -A ../../../pup.ts foreground` if not. Success! diff --git a/docs/src/examples/telemetry/README.md b/docs/src/examples/telemetry/README.md index a3bd5fd..acd5afc 100644 --- a/docs/src/examples/telemetry/README.md +++ b/docs/src/examples/telemetry/README.md @@ -59,7 +59,7 @@ telemetry.emit("another-process-id", "my-event", { data: { to: "send" } }) `cd` to `docs/examples/telemetry` directory. -Start example by running `pup run` if pup is installed, or something like `deno run -A ../../../pup.ts run` if not. +Start example by running `pup foreground` if pup is installed, or something like `deno run -A ../../../pup.ts foreground` if not. Now open another terminal and issue `pup status`, a brief overview of current status is shown, including memory usage. diff --git a/docs/src/examples/watcher/README.md b/docs/src/examples/watcher/README.md index e3dde4c..5db4366 100644 --- a/docs/src/examples/watcher/README.md +++ b/docs/src/examples/watcher/README.md @@ -21,8 +21,8 @@ current directory (configuration `watch: ["."]`). `cd` to `/docs/src/examples/watcher` directory. -Start example by running `pup run` if pup is installed, pup will automatically pick up the configuration in`pup.jsonc`. +Start example by running `pup foreground` if pup is installed, pup will automatically pick up the configuration in`pup.jsonc`. -Run something like `deno run -A ../../../pup.ts run` if pup is not installed globally. +Run something like `deno run -A ../../../pup.ts foreground` if pup is not installed globally. Success! diff --git a/docs/src/examples/worker/README.md b/docs/src/examples/worker/README.md index 805069b..10a2249 100644 --- a/docs/src/examples/worker/README.md +++ b/docs/src/examples/worker/README.md @@ -21,7 +21,7 @@ process using a worker. The process has logging configurations to write logs to `cd` to `/docs/src/examples/worker` directory. -Start example by running `pup run` if pup is installed, or something like `deno run -A ../../../pup.ts run` if not. +Start example by running `pup foreground` if pup is installed, or something like `deno run -A ../../../pup.ts foreground` if not. Success! diff --git a/docs/src/index.md b/docs/src/index.md index 39abcef..ffcf3f8 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -20,8 +20,8 @@ Pup is a powerful process manager for Deno, designed to simplify the management > **Note** Programmatic usage, process telemetry, and IPC are currently available only when running Deno client processes. { .note } -Pup is centered on a single configuration file, ideally named `pup.json` or `pup.jsonc`, which manages all aspects of the processes to be executed, including their execution methods and logging -handling. +Pup is centered on a single configuration file, `pup.json`, which manages all aspects of the processes to be executed, including their execution methods and logging handling. +[JSON5](https://github.com/json5/json5) syntax is supported. ## Quick Start diff --git a/docs/src/installation.md b/docs/src/installation.md index 0c29ed3..22aae2b 100644 --- a/docs/src/installation.md +++ b/docs/src/installation.md @@ -13,7 +13,7 @@ This section will guide you through the installation process of Pup. Before proceeding with the installation, ensure that you have the following installed on your system: -- Deno (version `1.30.x` or higher): You can install Deno by following the official Deno [instructions](https://deno.com/manual/getting_started/installation). +- Deno (version `1.38.x` or higher): You can install Deno by following the official Deno [instructions](https://deno.com/manual/getting_started/installation). ## Installing or upgrading Pup @@ -35,7 +35,7 @@ If you already have Pup installed and want to upgrade to the latest version, you pup upgrade --channel prerelease ``` -Both the `setup` and `upgrade` commands support the following parameters: +The `upgrade` command support the following parameters: - `--version`: Install, upgrade, or downgrade to a specific version. - `--channel `: Defaults to stable, but you can also install the `prerelease` or `canary` channel. diff --git a/docs/src/usage/configuration.md b/docs/src/usage/configuration.md index aed806f..37053b3 100644 --- a/docs/src/usage/configuration.md +++ b/docs/src/usage/configuration.md @@ -94,7 +94,7 @@ configuration file: - `console` (boolean): Set to true to enable logging to the console. Default is false. - `stdout` (string): The file path to write standard output logs. -- `stderr` (string): The file path to write standard error logs. +- `stderr` (string): The file path to write standard error logs. If omitted, stderr is redirected to stdout. - `decorateFiles` (boolean): Set to true to enable decoration in the output files. Default is false. - `decorate` (boolean): **Only available in global scope.** Set to true to enable decoration in the logs. Default is false. - `colors` (boolean): **Only available in global scope.** Set to true to enable colored output. Default is false. @@ -139,7 +139,7 @@ To change default behavior of the global watcher, use the following properties w - `interval` (number): The interval (in milliseconds) at which the watcher checks for file changes. Default is `1000`. - `exts` (array of strings): The file extensions to watch. Default is `["ts", "tsx", "js", "jsx", "json"]`. -- `match` (array of strings): The patterns to match for watched files. Default is `["**/_._"]`. +- `match` (array of strings): The patterns to match for watched files. Default is `["**/*.*"]`. - `skip` (array of strings): The patterns to exclude from watching. Default is `["**/.git/**"]`. ```json @@ -148,7 +148,7 @@ To change default behavior of the global watcher, use the following properties w "watcher": { "interval": 100, // default "exts": ["ts", "tsx", "js", "jsx", "json"], // default - "match": ["**/_._"], // default + "match": ["**/*.*"], // default "skip": ["**/.git/**"] // default }, @@ -194,8 +194,8 @@ To activate plugins, add your plugins to the configuration using this pattern: ## Validating the Configuration -To ensure your configuration is valid, just run `pup run` (or `pup run --config custom/path/to/config.json`). If using pup as a library, you can use the `validateConfiguration()` function provided by -the `/lib/core/configuration.ts` file. This function checks if the configuration adheres to the schema and will throw an error if it doesn't. +To ensure your configuration is valid, just run `pup foreground` (or `pup run --config custom/path/to/config.json`). If using pup as a library, you can use the `validateConfiguration()` function +provided by the `/lib/core/configuration.ts` file. This function checks if the configuration adheres to the schema and will throw an error if it doesn't. With a valid configuration in place, you're ready to use Pup to manage your processes. diff --git a/docs/src/usage/service.md b/docs/src/usage/service.md index 4d18fd6..11dc1f5 100644 --- a/docs/src/usage/service.md +++ b/docs/src/usage/service.md @@ -17,7 +17,7 @@ Follow the guide below to install Pup as a system service and launch at boot. Th ### Prerequisites -Ensure that you have a working environment set up so that you can run `pup run` with a `pup.json` in the current directory, or that you can start Pup using `pup run --config path/to/pup.json`. +Ensure that you have a working environment set up so that you can run `pup foreground` with a `pup.json` in the current directory, or that you can start Pup using `pup run --config path/to/pup.json`. Now there is two options, User Mode Installation, or System Installation. User Mode Installation is recommended as it rhymes best with Deno, which is installed for the current user. User mode is only supported with launchd or systemd. @@ -34,11 +34,11 @@ Replace `username` with your actual username. 2. Install Pup as a user mode service, named `pup`: -`pup install` +`pup enable-service` To install multiple services, provide a unique name for each instance: -`pup install --name my-service` +`pup enable-service --name my-service` ### System Mode Installation @@ -46,11 +46,11 @@ To install multiple services, provide a unique name for each instance: 1. Install Pup as a system service, by default named `pup`: -`pup install --system` +`pup enable-service --system` To install multiple services, provide a unique name for each instance: -`pup install --system --name my-service` +`pup enable-service --system --name my-service` 2. Follow the on-screen instructions to copy the generated configuration file to the correct location, and enable the service. @@ -59,8 +59,8 @@ To install multiple services, provide a unique name for each instance: Use the `pup [...flags]` command with the following methods and flags: - Methods: - - `install`: Installs the configured Pup instance as a system service, then verifies the installation by enabling and starting the service. Rolls back any changes on error. - - `uninstall`: Uninstall service + - `enable-service`: Installs the configured Pup instance as a system service, then verifies the installation by enabling and starting the service. Rolls back any changes on error. + - `disable-service`: Uninstall service - Flags: - `--config`: Specifies the configuration file for the instance to be installed, defaulting to `pup.json` or `pup.jsonc` in the current directory. @@ -144,7 +144,7 @@ Description=Pup After=network.target [Service] -ExecStart=/home/user/.deno/bin/deno run -A https://deno.land/x/pup/pup.ts run --config /path/to/your/pup.json +ExecStart=/home/user/.deno/bin/deno run -A https://deno.land/x/pup/`pup.ts foreground` --config /path/to/your/pup.json Restart=always [Install] diff --git a/lib/cli/config.ts b/lib/cli/config.ts index 8e66157..17e3435 100644 --- a/lib/cli/config.ts +++ b/lib/cli/config.ts @@ -7,7 +7,7 @@ */ import { Configuration, generateConfiguration, ProcessConfiguration } from "../core/configuration.ts" -import * as jsonc from "@std/jsonc" +import JSON5 from "npm:json5" import { join } from "@std/path" import { exists } from "@cross/fs" import { ArgsParser } from "@cross/utils" @@ -55,7 +55,7 @@ export async function appendConfigurationFile(configFile: string, checkedArgs: A let existingConfigurationObject try { const existingConfiguration = await Deno.readTextFile(configFile) - existingConfigurationObject = jsonc.parse(existingConfiguration) as unknown as Configuration + existingConfigurationObject = JSON5.parse(existingConfiguration) as unknown as Configuration } catch (e) { throw new Error("Could not read configuration file: " + e.message) } @@ -109,7 +109,7 @@ export async function removeFromConfigurationFile(configFile: string, checkedArg let existingConfigurationObject try { const existingConfiguration = await Deno.readTextFile(configFile) - existingConfigurationObject = jsonc.parse(existingConfiguration) as unknown as Configuration + existingConfigurationObject = JSON5.parse(existingConfiguration) as unknown as Configuration } catch (e) { throw new Error("Could not read configuration file.", e.message) } @@ -155,15 +155,15 @@ export async function findConfigFile(cwd: string, useConfigFile?: boolean, argum } } - // Try to find configuration file, jsonc first. Take cwd into account. + // Try to find configuration file, JSON5 first. Take cwd into account. let jsonPath = "./pup.json" - let jsoncPath = "./pup.jsonc" + let JSON5Path = "./pup.JSON5" if (cwd) { jsonPath = join(toResolvedAbsolutePath(cwd), jsonPath) - jsoncPath = join(toResolvedAbsolutePath(cwd), jsoncPath) + JSON5Path = join(toResolvedAbsolutePath(cwd), JSON5Path) } - if (await exists(jsoncPath)) { - return jsoncPath + if (await exists(JSON5Path)) { + return JSON5Path } else { return jsonPath } diff --git a/lib/cli/main.ts b/lib/cli/main.ts index 203fadb..e1c6e41 100644 --- a/lib/cli/main.ts +++ b/lib/cli/main.ts @@ -22,14 +22,14 @@ import { toPersistentPath, toResolvedAbsolutePath, toTempPath } from "../common/ import { exists, readFile } from "@cross/fs" // Import external dependencies -import * as jsonc from "@std/jsonc" +import JSON5 from "json5" import * as path from "@std/path" import { Logger } from "../core/logger.ts" import { args } from "@cross/utils/args" import { installService, uninstallService } from "@cross/service" -import { exit } from "@cross/utils" +import { Colors, exit } from "@cross/utils" import { chdir, cwd } from "@cross/fs" /** @@ -144,7 +144,18 @@ async function main() { const env = parsedArgs.getArray("env") || [] try { - await installService({ system, name, cmd, cwd, user, home, env }, parsedArgs.getBoolean("dry-run")) + const result = await installService({ system, name, cmd, cwd, user, home, env }, parsedArgs.getBoolean("dry-run")) + if (result.manualSteps && result.manualSteps.length) { + console.log(Colors.bold("To complete the installation, carry out these manual steps:")) + result.manualSteps.forEach((step, index) => { + console.log(Colors.cyan(`${index + 1}. ${step.text}`)) + if (step.command) { + console.log(" " + Colors.yellow("Command: ") + step.command) + } + }) + } else { + console.log(`Service ´${name}' successfully installed at '${result.servicePath}'.`) + } exit(0) } catch (e) { console.error(`Could not install service, error: ${e.message}`) @@ -154,10 +165,19 @@ async function main() { const system = parsedArgs.getBoolean("system") const name = parsedArgs.get("name") || "pup" const home = parsedArgs.get("home") - try { - await uninstallService({ system, name, home }) - console.log(`Service '${name}' uninstalled.`) + const result = await uninstallService({ system, name, home }) + if (result.manualSteps && result.manualSteps.length) { + console.log(Colors.bold("To complete the uninstallation, carry out these manual steps:")) + result.manualSteps.forEach((step, index) => { + console.log(Colors.cyan(`${index + 1}. ${step.text}`)) + if (step.command) { + console.log(" " + Colors.yellow("Command: ") + step.command) + } + }) + } else { + console.log(`Service '${name}' at '${result.servicePath}' is now uninstalled.`) + } exit(0) } catch (e) { console.error(`Could not uninstall service, error: ${e.message}`) @@ -169,8 +189,8 @@ async function main() { * Now, handle the argument to generate a new configuration file and exit */ if (baseArgument === "init") { - // Default new configuration file to pup.jsonc - const fallbackedConfigFile = configFile ?? "pup.jsonc" + // Default new configuration file to pup.json + const fallbackedConfigFile = configFile ?? "pup.json" if (await exists(fallbackedConfigFile)) { console.error(`Configuration file '${fallbackedConfigFile}' already exists, exiting.`) exit(1) @@ -234,7 +254,7 @@ async function main() { try { const rawConfig = await readFile(configFile) const rawConfigText = new TextDecoder().decode(rawConfig) - configuration = validateConfiguration(jsonc.parse(rawConfigText)) + configuration = validateConfiguration(JSON5.parse(rawConfigText)) } catch (e) { console.error(`Could not start, error reading or parsing configuration file '${configFile}': ${e.message}`) exit(1) diff --git a/lib/cli/output.ts b/lib/cli/output.ts index c38605d..0848adb 100644 --- a/lib/cli/output.ts +++ b/lib/cli/output.ts @@ -47,7 +47,7 @@ export function createFlagsMessage(externalInstaller: boolean): string { { long: "disable-service", description: "Uninstall pup service" }, { separator: "empty" }, { short: "-c", long: "--config ", description: "Optional. Use specific configuration file." }, - { description: "Default: ./pup.jsonc or ./pup.json" }, + { description: "Default: ./pup.json" }, { long: "--dry-run", description: "Generate and output service configuration, do not actually install the service" }, { long: "--system", description: "Optional. Install the service system-wide (requires root)." }, { long: "--cwd", description: "Optional. Set working directory for service" }, @@ -67,7 +67,7 @@ export function createFlagsMessage(externalInstaller: boolean): string { { long: "--severity ", description: "Optional. Filter logs based on the severity level." }, { long: "--start ", description: "Display logs after a specified timestamp." }, { long: "--end ", description: "Display logs before a specific timestamp." }, - { description: "Default: ./pup.jsonc or ./pup.json" }, + { description: "Default: ./pup.json" }, { separator: "empty" }, { content: "Configuration helpers", spanStart: 1 }, { separator: "empty" }, @@ -77,7 +77,7 @@ export function createFlagsMessage(externalInstaller: boolean): string { { description: "configure using the flags below." }, { separator: "empty" }, { short: "-c", long: "--config ", description: "Optional. Use specific configuration file." }, - { description: "Default: ./pup.jsonc or ./pup.json" }, + { description: "Default: ./pup.json" }, { short: "-I", long: "--id", description: "Id of the process to add/append/remove from configuration." }, { short: "-C", long: "--cmd", description: "Command to run, for complex commands use '--' then the command." }, { short: "-W", long: "--worker", description: "Worker script to run, any trailing arguments are passed to the worker" }, diff --git a/lib/common/utils.ts b/lib/common/utils.ts index cd29106..a7c8c7d 100644 --- a/lib/common/utils.ts +++ b/lib/common/utils.ts @@ -25,7 +25,7 @@ export function toResolvedAbsolutePath(path: string, cwdInput?: string) { */ export async function toTempPath(configFile: string) { const resolvedPath = parse(toResolvedAbsolutePath(configFile)) - const tempPath = toResolvedAbsolutePath(`${resolvedPath.dir}/.${resolvedPath.name}${resolvedPath.ext}-tmp`) + const tempPath = toResolvedAbsolutePath(`${resolvedPath.dir}/.pup/${resolvedPath.name}${resolvedPath.ext}-tmp`) await mkdir(tempPath, { recursive: true }) return tempPath } @@ -38,7 +38,7 @@ export async function toTempPath(configFile: string) { */ export async function toPersistentPath(configFile: string) { const resolvedPath = parse(toResolvedAbsolutePath(configFile)) - const persistentStoragePath = resolve(`${resolvedPath.dir}/.${resolvedPath.name}${resolvedPath.ext}-data`) + const persistentStoragePath = resolve(`${resolvedPath.dir}/.pup/${resolvedPath.name}${resolvedPath.ext}-data`) await mkdir(persistentStoragePath, { recursive: true }) return persistentStoragePath } diff --git a/lib/core/runner.ts b/lib/core/runner.ts index 85f4986..87a9a72 100644 --- a/lib/core/runner.ts +++ b/lib/core/runner.ts @@ -38,8 +38,8 @@ class Runner extends BaseRunner { runningCallback() - this.pipeToLogger("stdout", this.process.stdout()) - this.pipeToLogger("stderr", this.process.stderr()) + this.pipeToLogger("stdout", this.process.stdout() as ReadableStream) + this.pipeToLogger("stderr", this.process.stderr() as ReadableStream) const result = await this.waitForProcessEnd() diff --git a/telemetry.ts b/telemetry.ts index 9eed00a..2d9939b 100644 --- a/telemetry.ts +++ b/telemetry.ts @@ -31,7 +31,7 @@ import { EventEmitter, EventHandler } from "./lib/common/eventemitter.ts" import { FileIPC } from "./lib/common/ipc.ts" -import { dirExists } from "./lib/common/utils.ts" +import { exists, isDir } from "@cross/fs" export interface TelemetryData { sender: string @@ -85,7 +85,7 @@ export class PupTelemetry { const pupTempPath = Deno.env.get("PUP_TEMP_STORAGE") const pupProcessId = Deno.env.get("PUP_PROCESS_ID") - if (pupTempPath && (await dirExists(pupTempPath)) && pupProcessId) { + if (pupTempPath && (await exists(pupTempPath)) && pupProcessId) { const data: TelemetryData = { sender: pupProcessId, memory: Deno.memoryUsage(), @@ -102,7 +102,7 @@ export class PupTelemetry { const pupTempPath = Deno.env.get("PUP_TEMP_STORAGE") const pupProcessId = Deno.env.get("PUP_PROCESS_ID") - if (pupTempPath && (await dirExists(pupTempPath)) && pupProcessId) { + if (pupTempPath && (await isDir(pupTempPath)) && pupProcessId) { const ipcPath = `${pupTempPath}/.${pupProcessId}.ipc` // Process-specific IPC path this.ipc = new FileIPC(ipcPath) @@ -157,7 +157,7 @@ export class PupTelemetry { async emit(targetProcessId: string, event: string, eventData?: T) { const pupTempPath = Deno.env.get("PUP_TEMP_STORAGE") - if (pupTempPath && (await dirExists(pupTempPath)) && targetProcessId) { + if (pupTempPath && (await isDir(pupTempPath)) && targetProcessId) { const ipcPath = `${pupTempPath}/.${targetProcessId}.ipc` // Target process IPC path // Create a temporary IPC to send the message diff --git a/test/cli/args.test.ts b/test/cli/args.test.ts index 7931e6f..19dc9ce 100644 --- a/test/cli/args.test.ts +++ b/test/cli/args.test.ts @@ -56,7 +56,7 @@ Deno.test("String options and aliases are parsed correctly", () => { assertEquals(parsedArgs.get("cmd"), "command") assertEquals(parsedArgs.getBoolean("dry-run"), true) }) -/* + Deno.test("checkArguments should throw error when autostart argument is provided without init, append or --cmd", async () => { const args = new ArgsParser(["--cron"]) assertThrows( @@ -68,6 +68,7 @@ Deno.test("checkArguments should throw error when autostart argument is provided ) }) +/* Deno.test("checkArguments should throw error when cron argument is provided without init or append", async () => { const args = new ArgsParser(["cron"]) assertThrows( diff --git a/test/common/ipc.test.ts b/test/common/ipc.test.ts index eb1287c..250c24a 100644 --- a/test/common/ipc.test.ts +++ b/test/common/ipc.test.ts @@ -1,6 +1,6 @@ import { assertEquals } from "@std/assert" import { FileIPC } from "../../lib/common/ipc.ts" -import { fileExists } from "../../lib/common/utils.ts" +import { exists } from "@cross/fs" const TEST_FILE_PATH = "./test_data_FileIPC.ipctest" const TEST_STALE_LIMIT = 2000 @@ -10,7 +10,7 @@ Deno.test({ async fn() { const fileIPC = new FileIPC(TEST_FILE_PATH) await fileIPC.sendData("test data") - const fileExistsResult = await fileExists(TEST_FILE_PATH) + const fileExistsResult = await exists(TEST_FILE_PATH) assertEquals(fileExistsResult, true) await fileIPC.close() }, @@ -78,7 +78,7 @@ Deno.test({ const fileIPC = new FileIPC(TEST_FILE_PATH) await fileIPC.sendData("test data") await fileIPC.close() - const fileExistsResult = await fileExists(TEST_FILE_PATH) + const fileExistsResult = await exists(TEST_FILE_PATH) assertEquals(fileExistsResult, false) }, }) @@ -89,7 +89,7 @@ Deno.test({ const fileIPC = new FileIPC(TEST_FILE_PATH) await fileIPC.sendData("test data") await fileIPC.close(true) - const fileExistsResult = await fileExists(TEST_FILE_PATH) + const fileExistsResult = await exists(TEST_FILE_PATH) assertEquals(fileExistsResult, true) await Deno.remove(TEST_FILE_PATH) }, @@ -101,7 +101,7 @@ Deno.test({ const fileIPC = new FileIPC(TEST_FILE_PATH) await fileIPC.sendData("test data") await fileIPC.close(true) - const fileExistsResult = await fileExists(TEST_FILE_PATH) + const fileExistsResult = await exists(TEST_FILE_PATH) assertEquals(fileExistsResult, true) await Deno.remove(TEST_FILE_PATH) }, @@ -113,7 +113,7 @@ Deno.test({ const fileIPC = new FileIPC(TEST_FILE_PATH) await fileIPC.sendData("test data") await fileIPC.close(true) - const fileExistsResult = await fileExists(TEST_FILE_PATH) + const fileExistsResult = await exists(TEST_FILE_PATH) assertEquals(fileExistsResult, true) await Deno.remove(TEST_FILE_PATH) }, diff --git a/test/common/utils.test.ts b/test/common/utils.test.ts deleted file mode 100644 index 9731989..0000000 --- a/test/common/utils.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { dirExists, fileExists } from "../../lib/common/utils.ts" -import { assertEquals } from "@std/assert" - -Deno.test("dirExists - Directory exists", async () => { - const tempDir = await Deno.makeTempDir() - - const exists = await dirExists(tempDir) - assertEquals(exists, true) - - await Deno.remove(tempDir) -}) - -Deno.test("dirExists - Directory does not exist", async () => { - const tempDir = await Deno.makeTempDir() - await Deno.remove(tempDir) - - const exists = await dirExists(tempDir) - assertEquals(exists, false) -}) - -Deno.test("dirExists - Non-NotFound error occurs", async () => { - const invalidDir = "\0" - - let errorOccurred = false - try { - await dirExists(invalidDir) - } catch (_e) { - errorOccurred = true - } - assertEquals(errorOccurred, true) -}) - -Deno.test("dirExists - Path exists but is not a directory", async () => { - const tempFile = await Deno.makeTempFile() - - const exists = await dirExists(tempFile) - assertEquals(exists, false) - - await Deno.remove(tempFile) -}) - -Deno.test("fileExists - File exists", async () => { - const tempFile = await Deno.makeTempFile() - - const exists = await fileExists(tempFile) - assertEquals(exists, true) - - await Deno.remove(tempFile) -}) - -Deno.test("fileExists - File does not exist", async () => { - const tempFile = await Deno.makeTempFile() - await Deno.remove(tempFile) - - const exists = await fileExists(tempFile) - assertEquals(exists, false) -}) - -Deno.test("fileExists - Non-NotFound error occurs", async () => { - const invalidFile = "\0" - - let errorOccurred = false - try { - await fileExists(invalidFile) - } catch (_e) { - errorOccurred = true - } - assertEquals(errorOccurred, true) -}) - -Deno.test("fileExists - Path exists but is not a file", async () => { - const tempDir = await Deno.makeTempDir() - - const exists = await fileExists(tempDir) - assertEquals(exists, false) - - await Deno.remove(tempDir) -}) From 7ab4de90583d5f138d0d6c65e257ffa850e8e491 Mon Sep 17 00:00:00 2001 From: Hexagon Date: Sat, 13 Apr 2024 01:01:01 +0200 Subject: [PATCH 08/13] Work in progress --- deno.json | 1 + docs/src/changelog.md | 4 +- lib/core/configuration.ts | 2 +- test/cli/args.test.ts | 67 +++++----- test/cli/columns.test.ts | 4 +- test/cli/configuration.test.ts | 2 +- test/cli/output.test.ts | 8 +- test/common/eventemitter.test.ts | 10 +- test/common/ipc.test.ts | 173 +++++++++++------------- test/core/loadbalancer.test.ts | 12 +- test/core/logger.test.ts | 18 +-- test/core/plugin.test.ts | 26 ++-- test/core/pup.test.ts | 223 +++++++++++++++---------------- test/core/status.test.ts | 4 +- test/main.test.ts | 6 +- test/telemetry.test.ts | 4 +- 16 files changed, 269 insertions(+), 295 deletions(-) diff --git a/deno.json b/deno.json index 45c4f29..53e332e 100644 --- a/deno.json +++ b/deno.json @@ -22,6 +22,7 @@ "imports": { "@cross/fs": "jsr:@cross/fs@^0.0.9", "@cross/service": "jsr:@cross/service@^1.0.0", + "@cross/test": "jsr:@cross/test@^0.0.9", "@cross/utils": "jsr:@cross/utils@^0.10.0", "@hexagon/croner": "jsr:@hexagon/croner@^8.0.1", "@std/assert": "jsr:@std/assert@^0.221.0", diff --git a/docs/src/changelog.md b/docs/src/changelog.md index 9548437..fe1cebb 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -18,6 +18,7 @@ All notable changes to this project will be documented in this section. - [x] change(cli): Rename cli command `uninstall` to `disable-service` - [ ] change(logging): Make logs streaming by default. - [x] change(config): Support JSON5. +- [x] change(core): Move .pup.jsonc-tmp, .pup.jsonc-data into .pup ### Non-breaking @@ -25,6 +26,7 @@ All notable changes to this project will be documented in this section. - [x] fix(cli): Controlled output of manual rests after installing/uninstalling as service. - [x] fix(docs): Docs incorrectly stated that `**/_._` is default for watcher config. `**/*.*` is correct. - [ ] chore(docs): Add a PM2 migration guide. +- [x] fix(schema): Expect Record for process.env in json schema. ## Maintenance @@ -36,7 +38,7 @@ All notable changes to this project will be documented in this section. - [x] chore(deps): Replace `deno.land/x/zod` with `npm:zod` - [x] chore(deps): Utilize `@cross/utils` instead of Deno built-ins for cross runtime ansi console output, argument parsing, process management and more. - [x] chore(deps): Use `@cross/env` to handle enviroment variables across runtimes. -- [ ] chore(testing): Use `@cross/test` insted of Deno test runner, to utilize the native test runners of Node, Deno and Bun. +- [x] chore(testing): Use `@cross/test` insted of Deno test runner, to utilize the native test runners of Node, Deno and Bun. ## [1.0.0-rc.14] - 2024-04-07 diff --git a/lib/core/configuration.ts b/lib/core/configuration.ts index 465fe7c..932075d 100644 --- a/lib/core/configuration.ts +++ b/lib/core/configuration.ts @@ -120,7 +120,7 @@ const ConfigurationSchema = z.object({ cmd: z.optional(z.string()), worker: z.optional(z.array(z.string())), cwd: z.optional(z.string()), - env: z.optional(z.object({})), + env: z.optional(z.record(z.string())), cluster: z.optional(z.object({ instances: z.number().min(0).max(65535).default(1), commonPort: z.number().min(1).max(65535).optional(), diff --git a/test/cli/args.test.ts b/test/cli/args.test.ts index 19dc9ce..814ffec 100644 --- a/test/cli/args.test.ts +++ b/test/cli/args.test.ts @@ -4,8 +4,9 @@ import { spy } from "@std/testing/mock" import { Application } from "../../application.meta.ts" import { printHeader, printUsage } from "../../lib/cli/output.ts" import { ArgsParser } from "@cross/utils" +import { test } from "@cross/test" -Deno.test("Boolean options and aliases are parsed correctly", () => { +test("Boolean options and aliases are parsed correctly", () => { const inputArgs = [ "--version", "--help", @@ -30,7 +31,7 @@ Deno.test("Boolean options and aliases are parsed correctly", () => { assertEquals(parsedArgs.getBoolean("cmd"), true) }) -Deno.test("String options and aliases are parsed correctly", () => { +test("String options and aliases are parsed correctly", () => { const inputArgs = [ "--config", "config.json", @@ -57,7 +58,7 @@ Deno.test("String options and aliases are parsed correctly", () => { assertEquals(parsedArgs.getBoolean("dry-run"), true) }) -Deno.test("checkArguments should throw error when autostart argument is provided without init, append or --cmd", async () => { +test("checkArguments should throw error when autostart argument is provided without init, append or --cmd", async () => { const args = new ArgsParser(["--cron"]) assertThrows( () => { @@ -69,7 +70,7 @@ Deno.test("checkArguments should throw error when autostart argument is provided }) /* -Deno.test("checkArguments should throw error when cron argument is provided without init or append", async () => { +test("checkArguments should throw error when cron argument is provided without init or append", async () => { const args = new ArgsParser(["cron"]) assertThrows( () => { @@ -80,7 +81,7 @@ Deno.test("checkArguments should throw error when cron argument is provided with ) }) -Deno.test("checkArguments should throw error when terminate argument is provided without init or append", async () => { +test("checkArguments should throw error when terminate argument is provided without init or append", async () => { const args = { _: [], terminate: true } await assertThrows( () => { @@ -91,7 +92,7 @@ Deno.test("checkArguments should throw error when terminate argument is provided ) }) -Deno.test("checkArguments should throw error when watch argument is provided without init or append", async () => { +test("checkArguments should throw error when watch argument is provided without init or append", async () => { const args = { _: [], watch: "path" } await assertThrows( () => { @@ -102,7 +103,7 @@ Deno.test("checkArguments should throw error when watch argument is provided wit ) }) -Deno.test("checkArguments should throw error when cmd argument is provided without init, append or run", async () => { +test("checkArguments should throw error when cmd argument is provided without init, append or run", async () => { const args = { _: [], cmd: "command" } await assertThrows( () => { @@ -113,7 +114,7 @@ Deno.test("checkArguments should throw error when cmd argument is provided witho ) }) -Deno.test("checkArguments should throw error when worker argument is provided without init, append or run", async () => { +test("checkArguments should throw error when worker argument is provided without init, append or run", async () => { const args = new ArgsParser(["--worker", "command"]); await assertThrows( () => { @@ -124,7 +125,7 @@ Deno.test("checkArguments should throw error when worker argument is provided wi ) }) -Deno.test("checkArguments should throw error when init or append argument is provided without cmd", async () => { +test("checkArguments should throw error when init or append argument is provided without cmd", async () => { const args = new ArgsParser(["init"]); await assertThrows( () => { @@ -135,7 +136,7 @@ Deno.test("checkArguments should throw error when init or append argument is pro ) }) -Deno.test("checkArguments should throw error when both --cmd and -- is specified", async () => { +test("checkArguments should throw error when both --cmd and -- is specified", async () => { const args = { _: [], ["--"]: "hello", cmd: "hello world", init: true, id: "test" } await assertThrows( () => { @@ -146,7 +147,7 @@ Deno.test("checkArguments should throw error when both --cmd and -- is specified ) }) -Deno.test("checkArguments should throw error when id argument is missing with init or append argument", async () => { +test("checkArguments should throw error when id argument is missing with init or append argument", async () => { const args = { _: ["init"], cmd: "command" } await assertThrows( () => { @@ -157,7 +158,7 @@ Deno.test("checkArguments should throw error when id argument is missing with in ) }) -Deno.test("checkArguments should throw error when id argument is missing with init or append argument", async () => { +test("checkArguments should throw error when id argument is missing with init or append argument", async () => { const args = { _: ["append"], cmd: "command" } await assertThrows( () => { @@ -168,7 +169,7 @@ Deno.test("checkArguments should throw error when id argument is missing with in ) }) -Deno.test("checkArguments should throw error when id argument is missing with init or remove argument", async () => { +test("checkArguments should throw error when id argument is missing with init or remove argument", async () => { const args = { _: ["remove"], cmd: "command" } await assertThrows( () => { @@ -179,7 +180,7 @@ Deno.test("checkArguments should throw error when id argument is missing with in ) }) -Deno.test("printHeader should output the name, version, and repository of the application", () => { +test("printHeader should output the name, version, and repository of the application", () => { const expectedName = "pup" const expectedRepository = "https://github.com/hexagon/pup" const consoleSpy = spy(console, "log") @@ -191,7 +192,7 @@ Deno.test("printHeader should output the name, version, and repository of the ap consoleSpy.restore() }) -Deno.test("printUsage should output the usage of the application", () => { +test("printUsage should output the usage of the application", () => { const consoleSpy = spy(console, "log") printUsage() const expectedOutput = `Usage: ${Application.name} [OPTIONS...]` @@ -199,7 +200,7 @@ Deno.test("printUsage should output the usage of the application", () => { consoleSpy.restore() }) -Deno.test("checkArguments should return the provided arguments when they are valid", () => { +test("checkArguments should return the provided arguments when they are valid", () => { const expectedArgs = { _: ["init"], cmd: "command", @@ -209,7 +210,7 @@ Deno.test("checkArguments should return the provided arguments when they are val assertEquals(result, expectedArgs) }) -Deno.test("checkArguments should throw error when --env argument is provided without service install", async () => { +test("checkArguments should throw error when --env argument is provided without service install", async () => { const args = { _: [], env: "NODE_ENV=production" } await assertThrows( () => { @@ -220,7 +221,7 @@ Deno.test("checkArguments should throw error when --env argument is provided wit ) }) -Deno.test("checkArguments should return the provided arguments when service install and --env are used together", () => { +test("checkArguments should return the provided arguments when service install and --env are used together", () => { const expectedArgs = { _: ["install"], env: "NODE_ENV=production", @@ -229,7 +230,7 @@ Deno.test("checkArguments should return the provided arguments when service inst assertEquals(result, expectedArgs) }) -Deno.test("Collect env arguments formatted as KEY=VALUE", () => { +test("Collect env arguments formatted as KEY=VALUE", () => { const inputArgs = [ "--env", "KEY1=VALUE1", @@ -258,7 +259,7 @@ Deno.test("Collect env arguments formatted as KEY=VALUE", () => { assertEquals(parsedArgs, expectedArgs) }) -Deno.test("checkArguments should throw error when both --cmd and --worker are specified", async () => { +test("checkArguments should throw error when both --cmd and --worker are specified", async () => { const args = { _: [], cmd: "command", worker: "worker_script", init: true, id: "test" } await assertThrows( () => { @@ -269,67 +270,67 @@ Deno.test("checkArguments should throw error when both --cmd and --worker are sp ) }) -Deno.test("checkArguments should allow both --cwd and --id when used together", () => { +test("checkArguments should allow both --cwd and --id when used together", () => { const args = { _: ["init"], cmd: "command", id: "test", cwd: "cwd" } const result = checkArguments(args) assertEquals(result, args) }) -Deno.test("checkArguments should allow --terminate when used with --worker", () => { +test("checkArguments should allow --terminate when used with --worker", () => { const args = { _: ["init"], worker: "worker_script", id: "test", terminate: "terminate" } const result = checkArguments(args) assertEquals(result, args) }) -Deno.test("checkArguments should allow --watch when used with --worker", () => { +test("checkArguments should allow --watch when used with --worker", () => { const args = { _: ["init"], worker: "worker_script", id: "test", watch: "watched.ts" } const result = checkArguments(args) assertEquals(result, args) }) -Deno.test("checkArguments should allow --instances when used with init", () => { +test("checkArguments should allow --instances when used with init", () => { const args = { _: ["init"], cmd: "command", id: "test", instances: 2 } const result = checkArguments(args) assertEquals(result, args) }) -Deno.test("checkArguments should allow --instances when used with append", () => { +test("checkArguments should allow --instances when used with append", () => { const args = { _: ["append"], cmd: "command", id: "test", instances: 2 } const result = checkArguments(args) assertEquals(result, args) }) -Deno.test("checkArguments should allow --instances when used with run", () => { +test("checkArguments should allow --instances when used with run", () => { const args = { _: ["run"], cmd: "command", instances: 2 } const result = checkArguments(args) assertEquals(result, args) }) -Deno.test("checkArguments should allow --start-port when used with init", () => { +test("checkArguments should allow --start-port when used with init", () => { const args = { _: ["init"], cmd: "command", id: "test", startPort: 3000 } const result = checkArguments(args) assertEquals(result, args) }) -Deno.test("checkArguments should allow --start-port when used with append", () => { +test("checkArguments should allow --start-port when used with append", () => { const args = { _: ["append"], cmd: "command", id: "test", startPort: 3000 } const result = checkArguments(args) assertEquals(result, args) }) -Deno.test("checkArguments should allow --start-port when used with run", () => { +test("checkArguments should allow --start-port when used with run", () => { const args = { _: ["run"], cmd: "command", startPort: 3000 } const result = checkArguments(args) assertEquals(result, args) }) -Deno.test("checkArguments should allow --instances and --start-port when used together", () => { +test("checkArguments should allow --instances and --start-port when used together", () => { const args = { _: ["init"], cmd: "command", id: "test", instances: 2, startPort: 3000 } const result = checkArguments(args) assertEquals(result, args) }) -Deno.test("checkArguments should throw error when --start-port value is not a number", async () => { +test("checkArguments should throw error when --start-port value is not a number", async () => { const args = { _: ["init"], cmd: "command", id: "test", "start-port": "invalid" } await assertThrows( () => { @@ -340,7 +341,7 @@ Deno.test("checkArguments should throw error when --start-port value is not a nu ) }) -Deno.test("checkArguments should throw error when --instances value is not a number", async () => { +test("checkArguments should throw error when --instances value is not a number", async () => { const args = { _: ["init"], cmd: "command", id: "test", instances: "invalid" } await assertThrows( () => { @@ -351,7 +352,7 @@ Deno.test("checkArguments should throw error when --instances value is not a num ) }) -Deno.test("checkArguments should throw error when --common-port value is not a number", async () => { +test("checkArguments should throw error when --common-port value is not a number", async () => { const args = { _: ["init"], cmd: "command", id: "test", "common-port": "invalid" } await assertThrows( () => { diff --git a/test/cli/columns.test.ts b/test/cli/columns.test.ts index 74ca6f3..9ec930e 100644 --- a/test/cli/columns.test.ts +++ b/test/cli/columns.test.ts @@ -1,7 +1,7 @@ import { assertEquals } from "@std/assert" import { Column, Columns, SeparatorRow, TableRow } from "../../lib/cli/columns.ts" -Deno.test("Formats and pads Rows and Columns correctly", () => { +test("Formats and pads Rows and Columns correctly", () => { const columns: Column[] = [ { key: "name", header: "Name", align: "left" }, { key: "age", header: "Age", align: "center" }, @@ -19,7 +19,7 @@ Deno.test("Formats and pads Rows and Columns correctly", () => { assertEquals(result, expectedOutput) }) -Deno.test("Formats and pads SeparatorRow correctly", () => { +test("Formats and pads SeparatorRow correctly", () => { const columns: Column[] = [ { key: "name", header: "Name", align: "left" }, { key: "age", header: "Age", align: "center" }, diff --git a/test/cli/configuration.test.ts b/test/cli/configuration.test.ts index 3aae9b9..2750aa2 100644 --- a/test/cli/configuration.test.ts +++ b/test/cli/configuration.test.ts @@ -1,7 +1,7 @@ import { assertEquals } from "@std/assert" import { generateConfiguration } from "../../lib/core/configuration.ts" -Deno.test("Configuration - generateConfiguration creates a basic configuration", () => { +test("Configuration - generateConfiguration creates a basic configuration", () => { const id = "task" const cmd = "npm start" const cwd = "/path/to/project" diff --git a/test/cli/output.test.ts b/test/cli/output.test.ts index 02e8b6b..16d2d22 100644 --- a/test/cli/output.test.ts +++ b/test/cli/output.test.ts @@ -4,25 +4,25 @@ import { createFlagsMessage, createHeaderMessage, createUsageMessage } from "../ import { Application } from "../../application.meta.ts" import { assertEquals } from "@std/assert" -Deno.test("Should correctly create the header message", () => { +test("Should correctly create the header message", () => { const expected = Application.name + " " + Application.version + "\n" + Application.repository const actual = createHeaderMessage() assertEquals(actual, expected) }) -Deno.test("Should correctly create the usage message", () => { +test("Should correctly create the usage message", () => { const expected = `Usage: ${Application.name} [OPTIONS...]` const actual = createUsageMessage() assertEquals(actual, expected) }) -Deno.test("Should correctly create the flags message for external installer", () => { +test("Should correctly create the flags message for external installer", () => { const actual = createFlagsMessage(true) assertEquals(actual.includes("Restart process using IPC"), true) assertEquals(actual.includes("Install or upgrade pup"), false) }) -Deno.test("Should correctly create the flags message for non-external installer", () => { +test("Should correctly create the flags message for non-external installer", () => { const actual = createFlagsMessage(false) assertEquals(actual.includes("Restart process using IPC"), true) assertEquals(actual.includes("Install or upgrade pup"), true) diff --git a/test/common/eventemitter.test.ts b/test/common/eventemitter.test.ts index 5686e9d..c1c5d13 100644 --- a/test/common/eventemitter.test.ts +++ b/test/common/eventemitter.test.ts @@ -1,7 +1,7 @@ import { EventEmitter } from "../../lib/common/eventemitter.ts" import { assert, assertEquals } from "@std/assert" -Deno.test("EventEmitter - Add and trigger event listener", () => { +test("EventEmitter - Add and trigger event listener", () => { const eventEmitter = new EventEmitter() let called = false @@ -13,7 +13,7 @@ Deno.test("EventEmitter - Add and trigger event listener", () => { assert(called, "Event listener should be called") }) -Deno.test("EventEmitter - Trigger event listener with data", () => { +test("EventEmitter - Trigger event listener with data", () => { const eventEmitter = new EventEmitter() let receivedData: string | undefined @@ -25,7 +25,7 @@ Deno.test("EventEmitter - Trigger event listener with data", () => { assertEquals(receivedData, "Hello, World!", "Event listener should receive data") }) -Deno.test("EventEmitter - Remove event listener", () => { +test("EventEmitter - Remove event listener", () => { const eventEmitter = new EventEmitter() let called = false @@ -40,7 +40,7 @@ Deno.test("EventEmitter - Remove event listener", () => { assert(!called, "Event listener should not be called after being removed") }) -Deno.test("EventEmitter - Multiple listeners for same event", () => { +test("EventEmitter - Multiple listeners for same event", () => { const eventEmitter = new EventEmitter() let listener1Called = false let listener2Called = false @@ -60,7 +60,7 @@ Deno.test("EventEmitter - Multiple listeners for same event", () => { assert(listener2Called, "Listener 2 should be called") }) -Deno.test("EventEmitter - Multiple events with different listeners", () => { +test("EventEmitter - Multiple events with different listeners", () => { const eventEmitter = new EventEmitter() let testEventCalled = false let anotherEventCalled = false diff --git a/test/common/ipc.test.ts b/test/common/ipc.test.ts index 250c24a..ff06b12 100644 --- a/test/common/ipc.test.ts +++ b/test/common/ipc.test.ts @@ -1,120 +1,97 @@ import { assertEquals } from "@std/assert" import { FileIPC } from "../../lib/common/ipc.ts" import { exists } from "@cross/fs" +import { test } from "@cross/test" const TEST_FILE_PATH = "./test_data_FileIPC.ipctest" const TEST_STALE_LIMIT = 2000 -Deno.test({ - name: "FileIPC - sendData writes data to file", - async fn() { - const fileIPC = new FileIPC(TEST_FILE_PATH) - await fileIPC.sendData("test data") - const fileExistsResult = await exists(TEST_FILE_PATH) - assertEquals(fileExistsResult, true) - await fileIPC.close() - }, +test("FileIPC - sendData writes data to file", async () => { + const fileIPC = new FileIPC(TEST_FILE_PATH) + await fileIPC.sendData("test data") + const fileExistsResult = await exists(TEST_FILE_PATH) + assertEquals(fileExistsResult, true) + await fileIPC.close() }) -Deno.test({ - name: "FileIPC - receiveData returns an array of ValidatedMessages", - async fn() { - const fileIPC = new FileIPC(TEST_FILE_PATH) - await fileIPC.sendData("test data") - for await (const receivedMessages of fileIPC.receiveData()) { - assertEquals(receivedMessages.length, 1) - assertEquals(receivedMessages[0].pid, Deno.pid) - assertEquals(receivedMessages[0].data, "test data") - assertEquals(receivedMessages[0].errors.length, 0) - await fileIPC.close() - } - }, +test("FileIPC - receiveData returns an array of ValidatedMessages", async () => { + const fileIPC = new FileIPC(TEST_FILE_PATH) + await fileIPC.sendData("test data") + for await (const receivedMessages of fileIPC.receiveData()) { + assertEquals(receivedMessages.length, 1) + assertEquals(receivedMessages[0].pid, Deno.pid) + assertEquals(receivedMessages[0].data, "test data") + assertEquals(receivedMessages[0].errors.length, 0) + await fileIPC.close() + } }) -Deno.test({ - name: "FileIPC - receiveData removes stale messages", - async fn() { - const fileIPC = new FileIPC(TEST_FILE_PATH, TEST_STALE_LIMIT) - await fileIPC.sendData("test data 1") - await new Promise((resolve) => setTimeout(resolve, TEST_STALE_LIMIT + 100)) - await fileIPC.sendData("test data 2") - for await (const receivedMessages of fileIPC.receiveData()) { - assertEquals(receivedMessages.length, 2) - assertEquals(receivedMessages[0].pid, Deno.pid) - assertEquals(receivedMessages[0].data, null) - assertEquals(receivedMessages[0].errors.length, 1) - assertEquals(receivedMessages[0].errors[0], "Invalid data received: stale") - assertEquals(receivedMessages[1].pid, Deno.pid) - assertEquals(receivedMessages[1].data, "test data 2") - assertEquals(receivedMessages[1].errors.length, 0) - await fileIPC.close() - } - }, +test("FileIPC - receiveData removes stale messages", async () => { + const fileIPC = new FileIPC(TEST_FILE_PATH, TEST_STALE_LIMIT) + await fileIPC.sendData("test data 1") + await new Promise((resolve) => setTimeout(resolve, TEST_STALE_LIMIT + 100)) + await fileIPC.sendData("test data 2") + for await (const receivedMessages of fileIPC.receiveData()) { + assertEquals(receivedMessages.length, 2) + assertEquals(receivedMessages[0].pid, Deno.pid) + assertEquals(receivedMessages[0].data, null) + assertEquals(receivedMessages[0].errors.length, 1) + assertEquals(receivedMessages[0].errors[0], "Invalid data received: stale") + assertEquals(receivedMessages[1].pid, Deno.pid) + assertEquals(receivedMessages[1].data, "test data 2") + assertEquals(receivedMessages[1].errors.length, 0) + await fileIPC.close() + } }) -Deno.test({ - name: "FileIPC - receiveData handles invalid messages", - async fn() { - const fileIPC = new FileIPC(TEST_FILE_PATH) - await fileIPC.sendData("test data") - await fileIPC.sendData("a".repeat(fileIPC.MAX_DATA_LENGTH + 1)) - for await (const receivedMessages of fileIPC.receiveData()) { - assertEquals(receivedMessages.length, 2) - assertEquals(receivedMessages[0].pid, Deno.pid) - assertEquals(receivedMessages[0].data, "test data") - assertEquals(receivedMessages[0].errors.length, 0) - assertEquals(receivedMessages[1].pid, Deno.pid) - assertEquals(receivedMessages[1].data, null) - assertEquals(receivedMessages[1].errors.length, 1) - assertEquals(receivedMessages[1].errors[0], "Invalid data received: too long") - await fileIPC.close() - } - }, +test("FileIPC - receiveData handles invalid messages", async () => { + const fileIPC = new FileIPC(TEST_FILE_PATH) + await fileIPC.sendData("test data") + await fileIPC.sendData("a".repeat(fileIPC.MAX_DATA_LENGTH + 1)) + for await (const receivedMessages of fileIPC.receiveData()) { + assertEquals(receivedMessages.length, 2) + assertEquals(receivedMessages[0].pid, Deno.pid) + assertEquals(receivedMessages[0].data, "test data") + assertEquals(receivedMessages[0].errors.length, 0) + assertEquals(receivedMessages[1].pid, Deno.pid) + assertEquals(receivedMessages[1].data, null) + assertEquals(receivedMessages[1].errors.length, 1) + assertEquals(receivedMessages[1].errors[0], "Invalid data received: too long") + await fileIPC.close() + } }) -Deno.test({ - name: "FileIPC - close removes IPC file", - async fn() { - const fileIPC = new FileIPC(TEST_FILE_PATH) - await fileIPC.sendData("test data") - await fileIPC.close() - const fileExistsResult = await exists(TEST_FILE_PATH) - assertEquals(fileExistsResult, false) - }, +test("FileIPC - close removes IPC file", async () => { + const fileIPC = new FileIPC(TEST_FILE_PATH) + await fileIPC.sendData("test data") + await fileIPC.close() + const fileExistsResult = await exists(TEST_FILE_PATH) + assertEquals(fileExistsResult, false) }) -Deno.test({ - name: "FileIPC - close leaves IPC file when leaveFile option is true", - async fn() { - const fileIPC = new FileIPC(TEST_FILE_PATH) - await fileIPC.sendData("test data") - await fileIPC.close(true) - const fileExistsResult = await exists(TEST_FILE_PATH) - assertEquals(fileExistsResult, true) - await Deno.remove(TEST_FILE_PATH) - }, +test("FileIPC - close leaves IPC file when leaveFile option is true", async () => { + const fileIPC = new FileIPC(TEST_FILE_PATH) + await fileIPC.sendData("test data") + await fileIPC.close(true) + const fileExistsResult = await exists(TEST_FILE_PATH) + assertEquals(fileExistsResult, true) + await Deno.remove(TEST_FILE_PATH) }) -Deno.test({ - name: "FileIPC - close leaves IPC file when leaveFile option is true", - async fn() { - const fileIPC = new FileIPC(TEST_FILE_PATH) - await fileIPC.sendData("test data") - await fileIPC.close(true) - const fileExistsResult = await exists(TEST_FILE_PATH) - assertEquals(fileExistsResult, true) - await Deno.remove(TEST_FILE_PATH) - }, +test("FileIPC - close leaves IPC file when leaveFile option is true", async () => { + const fileIPC = new FileIPC(TEST_FILE_PATH) + await fileIPC.sendData("test data") + await fileIPC.close(true) + const fileExistsResult = await exists(TEST_FILE_PATH) + assertEquals(fileExistsResult, true) + await Deno.remove(TEST_FILE_PATH) }) -Deno.test({ - name: "FileIPC - close leaves IPC file when leaveFile option is true", - async fn() { - const fileIPC = new FileIPC(TEST_FILE_PATH) - await fileIPC.sendData("test data") - await fileIPC.close(true) - const fileExistsResult = await exists(TEST_FILE_PATH) - assertEquals(fileExistsResult, true) - await Deno.remove(TEST_FILE_PATH) - }, +test("FileIPC - close leaves IPC file when leaveFile option is true", async () => { + const fileIPC = new FileIPC(TEST_FILE_PATH) + await fileIPC.sendData("test data") + await fileIPC.close(true) + const fileExistsResult = await exists(TEST_FILE_PATH) + assertEquals(fileExistsResult, true) + await Deno.remove(TEST_FILE_PATH) }) diff --git a/test/core/loadbalancer.test.ts b/test/core/loadbalancer.test.ts index cf006a4..6a3b55c 100644 --- a/test/core/loadbalancer.test.ts +++ b/test/core/loadbalancer.test.ts @@ -43,7 +43,7 @@ class MockConn implements Deno.Conn { } } -Deno.test("LoadBalancer - Initialization", () => { +test("LoadBalancer - Initialization", () => { const backends: Backend[] = [ { host: "backend1.example.com", port: 80 }, { host: "backend2.example.com", port: 80 }, @@ -54,14 +54,14 @@ Deno.test("LoadBalancer - Initialization", () => { loadBalancer.close() }) -Deno.test("LoadBalancer - Throws Error When No Backends are Provided", () => { +test("LoadBalancer - Throws Error When No Backends are Provided", () => { const backends: Backend[] = [] assertThrows(() => { new LoadBalancer(backends, BalancingStrategy.ROUND_ROBIN, 120, loggerCallback) }) }) -Deno.test("LoadBalancer - Initializes with Backends Correctly", () => { +test("LoadBalancer - Initializes with Backends Correctly", () => { const backends: Backend[] = [ { host: "192.168.1.1", port: 8080 }, { host: "192.168.1.2", port: 8080 }, @@ -81,7 +81,7 @@ Deno.test("LoadBalancer - Initializes with Backends Correctly", () => { loadBalancer.close() }) -Deno.test("LoadBalancer - Selects Backend with ROUND_ROBIN Strategy", () => { +test("LoadBalancer - Selects Backend with ROUND_ROBIN Strategy", () => { const backends: Backend[] = [ { host: "192.168.1.1", port: 8080 }, { host: "192.168.1.2", port: 8080 }, @@ -99,7 +99,7 @@ Deno.test("LoadBalancer - Selects Backend with ROUND_ROBIN Strategy", () => { loadBalancer.close() }) -Deno.test("LoadBalancer - Selects Backend with IP_HASH Strategy", () => { +test("LoadBalancer - Selects Backend with IP_HASH Strategy", () => { const backends: Backend[] = [ { host: "192.168.1.1", port: 8080 }, { host: "192.168.1.2", port: 8080 }, @@ -116,7 +116,7 @@ Deno.test("LoadBalancer - Selects Backend with IP_HASH Strategy", () => { loadBalancer.close() }) -Deno.test("LoadBalancer - Selects Backend with LEAST_CONNECTIONS Strategy", () => { +test("LoadBalancer - Selects Backend with LEAST_CONNECTIONS Strategy", () => { const backends: Backend[] = [ { host: "192.168.1.1", port: 8080 }, { host: "192.168.1.2", port: 8080 }, diff --git a/test/core/logger.test.ts b/test/core/logger.test.ts index 79e2f01..81f725c 100644 --- a/test/core/logger.test.ts +++ b/test/core/logger.test.ts @@ -2,7 +2,7 @@ import { assertEquals, assertGreater } from "@std/assert" import { AttachedLogger, LogEventData, Logger } from "../../lib/core/logger.ts" import { ProcessConfiguration } from "../../mod.ts" -Deno.test("Logger - Creation with Global Configuration", () => { +test("Logger - Creation with Global Configuration", () => { const globalConfig = { console: false, colors: true, @@ -16,7 +16,7 @@ Deno.test("Logger - Creation with Global Configuration", () => { assertEquals(logger instanceof Logger, true) }) -Deno.test("Logger - Attachment of External Logger", () => { +test("Logger - Attachment of External Logger", () => { let externalLoggerCalled = false let externalLoggerText = "" const expectedExteralLoggerText = "Testing attached logger" @@ -39,7 +39,7 @@ Deno.test("Logger - Attachment of External Logger", () => { assertEquals(externalLoggerText, expectedExteralLoggerText) }) -Deno.test("Logger - Logging with Different Methods", () => { +test("Logger - Logging with Different Methods", () => { const logger = new Logger({ console: false }) logger.log("test", "Testing log method") @@ -50,7 +50,7 @@ Deno.test("Logger - Logging with Different Methods", () => { assertEquals(true, true) // This is just to assert that the test passed if no errors are thrown }) -Deno.test("Logger - Logging Line Larger than KV Limit", async () => { +test("Logger - Logging Line Larger than KV Limit", async () => { const tempStore = Deno.makeTempFileSync() const logger = new Logger({ console: false }, tempStore) @@ -81,7 +81,7 @@ Deno.test("Logger - Logging Line Larger than KV Limit", async () => { await Deno.remove(tempStore) }) -Deno.test("Logger - File Writing with writeFile Method", async () => { +test("Logger - File Writing with writeFile Method", async () => { const logger = new Logger({ console: false }) const testFileName = "test_writeFile.log" const testText = "Testing writeFile" @@ -93,7 +93,7 @@ Deno.test("Logger - File Writing with writeFile Method", async () => { await Deno.remove(testFileName) }) -Deno.test("Logger - getLogContents: Fetch all logs", async () => { +test("Logger - getLogContents: Fetch all logs", async () => { const tempStore = await Deno.makeTempDir() + "/.store" const logger = new Logger({}, tempStore) @@ -112,7 +112,7 @@ Deno.test("Logger - getLogContents: Fetch all logs", async () => { assertEquals(logs, expectedLogs) }) -Deno.test("Logger - getLogContents: Fetch logs by process ID", async () => { +test("Logger - getLogContents: Fetch logs by process ID", async () => { const tempStore = await Deno.makeTempDir() + "/.store" const logger = new Logger({}, tempStore) @@ -132,7 +132,7 @@ Deno.test("Logger - getLogContents: Fetch logs by process ID", async () => { assertEquals(logs, expectedLogs) }) -Deno.test("Logger - getLogContents: Fetch logs by time range", async () => { +test("Logger - getLogContents: Fetch logs by time range", async () => { const tempStore = await Deno.makeTempDir() + "/.store" const logger = new Logger({}, tempStore) @@ -153,7 +153,7 @@ Deno.test("Logger - getLogContents: Fetch logs by time range", async () => { assertEquals(logs, expectedLogs) }) -Deno.test("Logger - getLogContents: Fetch logs by process ID and time range", async () => { +test("Logger - getLogContents: Fetch logs by process ID and time range", async () => { const tempStore = await Deno.makeTempDir() + "/.store" const logger = new Logger({}, tempStore) diff --git a/test/core/plugin.test.ts b/test/core/plugin.test.ts index 4bc783d..bdea0fc 100644 --- a/test/core/plugin.test.ts +++ b/test/core/plugin.test.ts @@ -7,7 +7,7 @@ const minimalPupConfiguration = { processes: [], } -Deno.test("Plugin - Load and verify missing meta.name", async () => { +test("Plugin - Load and verify missing meta.name", async () => { const pup = await Pup.init(minimalPupConfiguration) const pluginConfiguration: PluginConfiguration = { url: "../../test/core/test-data/test_plugin_name.ts", @@ -18,7 +18,7 @@ Deno.test("Plugin - Load and verify missing meta.name", async () => { assertThrows(() => plugin.verify(), Error, "Plugin missing meta.name") }) -Deno.test("Plugin - Load and verify missing meta.repository", async () => { +test("Plugin - Load and verify missing meta.repository", async () => { const pup = await Pup.init(minimalPupConfiguration) const pluginConfiguration: PluginConfiguration = { url: "../../test/core/test-data/test_plugin_repository.ts", @@ -29,7 +29,7 @@ Deno.test("Plugin - Load and verify missing meta.repository", async () => { assertThrows(() => plugin.verify(), Error, "Plugin missing meta.repository") }) -Deno.test("Plugin - Load and verify missing meta.version", async () => { +test("Plugin - Load and verify missing meta.version", async () => { const pup = await Pup.init(minimalPupConfiguration) const pluginConfiguration: PluginConfiguration = { url: "../../test/core/test-data/test_plugin_version.ts", @@ -40,7 +40,7 @@ Deno.test("Plugin - Load and verify missing meta.version", async () => { assertThrows(() => plugin.verify(), Error, "Plugin missing meta.version") }) -Deno.test("Plugin - Load and verify missing meta.api", async () => { +test("Plugin - Load and verify missing meta.api", async () => { const pup = await Pup.init(minimalPupConfiguration) const pluginConfiguration: PluginConfiguration = { url: "../../test/core/test-data/test_plugin_api.ts", @@ -51,7 +51,7 @@ Deno.test("Plugin - Load and verify missing meta.api", async () => { assertThrows(() => plugin.verify(), Error, "Plugin missing meta.api") }) -Deno.test("Plugin - Load and verify unsupported API version", async () => { +test("Plugin - Load and verify unsupported API version", async () => { const pup = await Pup.init(minimalPupConfiguration) const pluginConfiguration: PluginConfiguration = { url: "../../test/core/test-data/test_plugin_unsupported_api.ts", @@ -62,7 +62,7 @@ Deno.test("Plugin - Load and verify unsupported API version", async () => { assertThrows(() => plugin.verify(), Error, "Plugin version not supported") }) -Deno.test("Plugin - Test default hook implementation", async () => { +test("Plugin - Test default hook implementation", async () => { const pup = await Pup.init(minimalPupConfiguration) const pluginConfiguration: PluginConfiguration = { url: "../../test/core/test-data/test_plugin_valid.ts", @@ -75,7 +75,7 @@ Deno.test("Plugin - Test default hook implementation", async () => { assertEquals(result, false, "Default hook implementation should return false") }) -Deno.test("PluginApi - Check temporaryStorage path - undefined", async () => { +test("PluginApi - Check temporaryStorage path - undefined", async () => { const pup = await Pup.init(minimalPupConfiguration) const pluginApi = new PluginApi(pup) @@ -92,7 +92,7 @@ Deno.test("PluginApi - Check temporaryStorage path - undefined", async () => { ) }) -Deno.test("PluginApi - Check persistentStorage path - undefined", async () => { +test("PluginApi - Check persistentStorage path - undefined", async () => { const pup = await Pup.init(minimalPupConfiguration) const pluginApi = new PluginApi(pup) @@ -109,7 +109,7 @@ Deno.test("PluginApi - Check persistentStorage path - undefined", async () => { ) }) -Deno.test("PluginApi - Check configFilePath - undefined", async () => { +test("PluginApi - Check configFilePath - undefined", async () => { const pup = await Pup.init(minimalPupConfiguration) const pluginApi = new PluginApi(pup) @@ -126,7 +126,7 @@ Deno.test("PluginApi - Check configFilePath - undefined", async () => { ) }) -Deno.test("PluginApi - Check configFilePath - set", async () => { +test("PluginApi - Check configFilePath - set", async () => { const pup = await Pup.init(minimalPupConfiguration, "./test/core/test-data/test.json") const pluginApi = new PluginApi(pup) @@ -143,7 +143,7 @@ Deno.test("PluginApi - Check configFilePath - set", async () => { ) }) -Deno.test("PluginApi - Check temporaryStorage path - set", async () => { +test("PluginApi - Check temporaryStorage path - set", async () => { const pup = await Pup.init(minimalPupConfiguration, "./test/core/test-data/test.json") const pluginApi = new PluginApi(pup) @@ -158,7 +158,7 @@ Deno.test("PluginApi - Check temporaryStorage path - set", async () => { ) }) -Deno.test("PluginApi - Check persistentStorage path - set", async () => { +test("PluginApi - Check persistentStorage path - set", async () => { const pup = await Pup.init(minimalPupConfiguration, "./test/core/test-data/test.json") const pluginApi = new PluginApi(pup) @@ -174,7 +174,7 @@ Deno.test("PluginApi - Check persistentStorage path - set", async () => { ) }) -Deno.test("Plugin - Test signal listener", async () => { +test("Plugin - Test signal listener", async () => { const pup = await Pup.init(minimalPupConfiguration) const pluginConfiguration: PluginConfiguration = { url: "../../test/core/test-data/test_signal_listener.ts", diff --git a/test/core/pup.test.ts b/test/core/pup.test.ts index 3618b0e..ec50fc5 100644 --- a/test/core/pup.test.ts +++ b/test/core/pup.test.ts @@ -8,122 +8,115 @@ import { Configuration } from "../../lib/core/configuration.ts" import { ProcessState } from "../../lib/core/process.ts" import { Pup } from "../../lib/core/pup.ts" import { assertEquals, assertNotEquals } from "@std/assert" - -Deno.test({ - name: "Create test process. Test start, block, stop, start, unblock, start in sequence.", - sanitizeExit: false, - fn: async () => { - const TEST_PROCESS_ID = "test-1" - const TEST_PROCESS_COMMAND = "deno run -A lib/test/core/test-data/test_process.ts" - - const config: Configuration = { - processes: [ - { - "id": TEST_PROCESS_ID, - "cmd": TEST_PROCESS_COMMAND, - }, - ], - } - const pup = new Pup(config) - await pup.init() - - // Find process, assert existance - const testProcess = pup.processes.findLast((p) => p.getConfig().id === TEST_PROCESS_ID) - assertNotEquals(testProcess, undefined) - assertEquals(testProcess?.getStatus().status, ProcessState.CREATED) - - // Start process, assert started - const startResult = pup.start(TEST_PROCESS_ID, "test") - assertEquals(startResult, true) - assertEquals(testProcess?.getStatus().status, ProcessState.RUNNING) - - // Stop process, assert stopped - const stopResult = await pup.stop(TEST_PROCESS_ID, "test") - assertEquals(stopResult, true) - assertEquals(testProcess?.getStatus().status, ProcessState.ERRORED) - - // Block process, assert blocked - const blockResult = pup.block(TEST_PROCESS_ID, "test") - assertEquals(blockResult, true) - assertEquals(testProcess?.getStatus().blocked, true) - - // Start process, assert failed - const startResult2 = pup.start(TEST_PROCESS_ID, "test") - assertEquals(startResult2, false) - assertEquals(testProcess?.getStatus().status, ProcessState.ERRORED) - - // Unblock process, assert unblocked - const unblockResult = pup.unblock(TEST_PROCESS_ID, "test") - assertEquals(unblockResult, true) - assertEquals(testProcess?.getStatus().blocked, false) - - // Start process, assert started - const startResult3 = pup.start(TEST_PROCESS_ID, "test") - assertEquals(startResult3, true) - assertEquals(testProcess?.getStatus().status, ProcessState.RUNNING) - - // Terminate pup instantly - await pup.terminate(0) - }, +import { test } from "@cross/test" + +test("Create test process. Test start, block, stop, start, unblock, start in sequence.", async () => { + const TEST_PROCESS_ID = "test-1" + const TEST_PROCESS_COMMAND = "deno run -A lib/test/core/test-data/test_process.ts" + + const config: Configuration = { + processes: [ + { + "id": TEST_PROCESS_ID, + "cmd": TEST_PROCESS_COMMAND, + }, + ], + } + const pup = new Pup(config) + await pup.init() + + // Find process, assert existance + const testProcess = pup.processes.findLast((p) => p.getConfig().id === TEST_PROCESS_ID) + assertNotEquals(testProcess, undefined) + assertEquals(testProcess?.getStatus().status, ProcessState.CREATED) + + // Start process, assert started + const startResult = pup.start(TEST_PROCESS_ID, "test") + assertEquals(startResult, true) + assertEquals(testProcess?.getStatus().status, ProcessState.RUNNING) + + // Stop process, assert stopped + const stopResult = await pup.stop(TEST_PROCESS_ID, "test") + assertEquals(stopResult, true) + assertEquals(testProcess?.getStatus().status, ProcessState.ERRORED) + + // Block process, assert blocked + const blockResult = pup.block(TEST_PROCESS_ID, "test") + assertEquals(blockResult, true) + assertEquals(testProcess?.getStatus().blocked, true) + + // Start process, assert failed + const startResult2 = pup.start(TEST_PROCESS_ID, "test") + assertEquals(startResult2, false) + assertEquals(testProcess?.getStatus().status, ProcessState.ERRORED) + + // Unblock process, assert unblocked + const unblockResult = pup.unblock(TEST_PROCESS_ID, "test") + assertEquals(unblockResult, true) + assertEquals(testProcess?.getStatus().blocked, false) + + // Start process, assert started + const startResult3 = pup.start(TEST_PROCESS_ID, "test") + assertEquals(startResult3, true) + assertEquals(testProcess?.getStatus().status, ProcessState.RUNNING) + + // Terminate pup instantly + await pup.terminate(0) }) -Deno.test({ - name: "Create test cluster. Test start, block, stop, start, unblock, start in sequence.", - sanitizeExit: false, - fn: async () => { - const TEST_PROCESS_ID = "test-2" - const TEST_PROCESS_COMMAND = "deno run -A lib/test/core/test-data/test_process.ts" - - const config: Configuration = { - processes: [ - { - "id": TEST_PROCESS_ID, - "cmd": TEST_PROCESS_COMMAND, - "cluster": { - "instances": 3, - }, +test("Create test cluster. Test start, block, stop, start, unblock, start in sequence.", async () => { + const TEST_PROCESS_ID = "test-2" + const TEST_PROCESS_COMMAND = "deno run -A lib/test/core/test-data/test_process.ts" + + const config: Configuration = { + processes: [ + { + "id": TEST_PROCESS_ID, + "cmd": TEST_PROCESS_COMMAND, + "cluster": { + "instances": 3, }, - ], - } - const pup = new Pup(config) - await pup.init() - - // Find process, assert existance - const testProcess = pup.processes.findLast((p) => p.getConfig().id === TEST_PROCESS_ID) - assertNotEquals(testProcess, undefined) - assertEquals(testProcess?.getStatus().status, ProcessState.CREATED) - - // Start process, assert started - const startResult = pup.start(TEST_PROCESS_ID, "test") - assertEquals(startResult, true) - assertEquals(testProcess?.getStatus().status, ProcessState.RUNNING) - - // Stop process, assert finished - const stopResult = await pup.stop(TEST_PROCESS_ID, "test") - assertEquals(stopResult, true) - assertEquals(testProcess?.getStatus().status, ProcessState.ERRORED) - - // Block process, assert blocked - const blockResult = pup.block(TEST_PROCESS_ID, "test") - assertEquals(blockResult, true) - assertEquals(testProcess?.getStatus().blocked, true) - - // Start process, assert failed - const startResult2 = pup.start(TEST_PROCESS_ID, "test") - assertEquals(startResult2, false) - assertEquals(testProcess?.getStatus().status, ProcessState.ERRORED) - - // Unblock process, assert unblocked - const unblockResult = pup.unblock(TEST_PROCESS_ID, "test") - assertEquals(unblockResult, true) - assertEquals(testProcess?.getStatus().blocked, false) - - // Start process, assert started - const startResult3 = pup.start(TEST_PROCESS_ID, "test") - assertEquals(startResult3, true) - assertEquals(testProcess?.getStatus().status, ProcessState.RUNNING) - - // Terminate pup instantly - await pup.terminate(0) - }, + }, + ], + } + const pup = new Pup(config) + await pup.init() + + // Find process, assert existance + const testProcess = pup.processes.findLast((p) => p.getConfig().id === TEST_PROCESS_ID) + assertNotEquals(testProcess, undefined) + assertEquals(testProcess?.getStatus().status, ProcessState.CREATED) + + // Start process, assert started + const startResult = pup.start(TEST_PROCESS_ID, "test") + assertEquals(startResult, true) + assertEquals(testProcess?.getStatus().status, ProcessState.RUNNING) + + // Stop process, assert finished + const stopResult = await pup.stop(TEST_PROCESS_ID, "test") + assertEquals(stopResult, true) + assertEquals(testProcess?.getStatus().status, ProcessState.ERRORED) + + // Block process, assert blocked + const blockResult = pup.block(TEST_PROCESS_ID, "test") + assertEquals(blockResult, true) + assertEquals(testProcess?.getStatus().blocked, true) + + // Start process, assert failed + const startResult2 = pup.start(TEST_PROCESS_ID, "test") + assertEquals(startResult2, false) + assertEquals(testProcess?.getStatus().status, ProcessState.ERRORED) + + // Unblock process, assert unblocked + const unblockResult = pup.unblock(TEST_PROCESS_ID, "test") + assertEquals(unblockResult, true) + assertEquals(testProcess?.getStatus().blocked, false) + + // Start process, assert started + const startResult3 = pup.start(TEST_PROCESS_ID, "test") + assertEquals(startResult3, true) + assertEquals(testProcess?.getStatus().status, ProcessState.RUNNING) + + // Terminate pup instantly + await pup.terminate(0) }) diff --git a/test/core/status.test.ts b/test/core/status.test.ts index c83c2ec..e3d4638 100644 --- a/test/core/status.test.ts +++ b/test/core/status.test.ts @@ -3,13 +3,13 @@ import { assertEquals } from "@std/assert" const TEST_FILE_PATH = "./test_data_Status.jsontest" -Deno.test("Status - Should create an instance with statusFileName property if provided", () => { +test("Status - Should create an instance with statusFileName property if provided", () => { const expectedFileName = TEST_FILE_PATH const status = new Status(expectedFileName) assertEquals(status["storeName"], expectedFileName) }) -Deno.test("Status - Should not have statusFileName property if not provided", () => { +test("Status - Should not have statusFileName property if not provided", () => { const status = new Status() assertEquals(status["storeName"], undefined) }) diff --git a/test/main.test.ts b/test/main.test.ts index 3fd7d9b..4e7d291 100644 --- a/test/main.test.ts +++ b/test/main.test.ts @@ -2,7 +2,7 @@ import { main } from "../lib/main.ts" import { assertSpyCall, spy } from "@std/testing/mock" -Deno.test("main: exit with --version flag", async () => { +test("main: exit with --version flag", async () => { const exitSpy = spy(Deno, "exit") const args = ["--version"] @@ -13,7 +13,7 @@ Deno.test("main: exit with --version flag", async () => { exitSpy.restore() }) -Deno.test("main: exit with --help flag", async () => { +test("main: exit with --help flag", async () => { const exitSpy = spy(Deno, "exit") const args = ["--help"] @@ -24,7 +24,7 @@ Deno.test("main: exit with --help flag", async () => { exitSpy.restore() }) -Deno.test("main: exit when no configuration file found", async () => { +test("main: exit when no configuration file found", async () => { const exitSpy = spy(Deno, "exit") const originalFileExists = Deno.stat diff --git a/test/telemetry.test.ts b/test/telemetry.test.ts index 26985e9..85a8ade 100644 --- a/test/telemetry.test.ts +++ b/test/telemetry.test.ts @@ -2,7 +2,7 @@ import { assertEquals } from "@std/assert" import { PupTelemetry } from "../telemetry.ts" -Deno.test("PupTelemetry - Singleton pattern", () => { +test("PupTelemetry - Singleton pattern", () => { const telemetry1 = new PupTelemetry() const telemetry2 = new PupTelemetry() const telemetry3 = new PupTelemetry() @@ -16,7 +16,7 @@ Deno.test("PupTelemetry - Singleton pattern", () => { }) // deno-lint-ignore require-await -Deno.test("PupTelemetry - Emitting messages", async () => { +test("PupTelemetry - Emitting messages", async () => { const telemetry = new PupTelemetry() const eventData = { test: "data" } From cdea3d348681432e8b7de8946b1ba7ae92073aae Mon Sep 17 00:00:00 2001 From: Hexagon Date: Sat, 13 Apr 2024 22:10:06 +0200 Subject: [PATCH 09/13] Progress --- docs/src/changelog.md | 10 +++--- lib/cli/main.ts | 14 ++++---- lib/cli/output.ts | 2 +- lib/core/pup.ts | 2 -- test/cli/args.test.ts | 56 ++++++++++++++++---------------- test/cli/columns.test.ts | 1 + test/cli/configuration.test.ts | 1 + test/cli/output.test.ts | 5 +-- test/common/eventemitter.test.ts | 1 + test/core/loadbalancer.test.ts | 1 + test/core/logger.test.ts | 1 + test/core/plugin.test.ts | 7 ++-- test/core/status.test.ts | 1 + test/telemetry.test.ts | 1 + 14 files changed, 54 insertions(+), 49 deletions(-) diff --git a/docs/src/changelog.md b/docs/src/changelog.md index fe1cebb..75c97c7 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -13,16 +13,16 @@ All notable changes to this project will be documented in this section. ### Breaking Changes -- [x] change(cli): Rename cli command `run` to `foreground` -- [x] change(cli): Rename cli command `install` to `enable-service` -- [x] change(cli): Rename cli command `uninstall` to `disable-service` -- [ ] change(logging): Make logs streaming by default. +- [x] change(cli): Rename cli command run to foreground to not be confused with starting as a service +- [x] change(cli): Rename cli command install to enable-service to separate from actual pup installation +- [x] change(cli): Rename cli command uninstall to disable-service to separate from actual pup uninstallation +- [ ] change(logging): Make `pup logs` streaming by default. - [x] change(config): Support JSON5. - [x] change(core): Move .pup.jsonc-tmp, .pup.jsonc-data into .pup ### Non-breaking -- [ ] fix(core): Foreground command does not keep an autostarted process running. +- [x] fix(core): Foreground command did not keep an autostarted process running, fixed by refing the watchdog timer. - [x] fix(cli): Controlled output of manual rests after installing/uninstalling as service. - [x] fix(docs): Docs incorrectly stated that `**/_._` is default for watcher config. `**/*.*` is correct. - [ ] chore(docs): Add a PM2 migration guide. diff --git a/lib/cli/main.ts b/lib/cli/main.ts index e1c6e41..2f1638c 100644 --- a/lib/cli/main.ts +++ b/lib/cli/main.ts @@ -138,7 +138,7 @@ async function main() { const name = parsedArgs.get("name") || "pup" const config = parsedArgs.get("config") const cwd = parsedArgs.get("cwd") - const cmd = `pup run ${config ? `--config ${config}` : ""}` + const cmd = `pup foreground ${config ? `--config ${config}` : ""}` const user = parsedArgs.get("user") const home = parsedArgs.get("home") const env = parsedArgs.getArray("env") || [] @@ -287,7 +287,7 @@ async function main() { // Add a new condition for "logs" base command if (baseArgument === "logs") { const logStore = `${await toPersistentPath(configFile as string)}/.main.log` - const logger = new Logger(configuration.logger || {}, logStore) + const logger = new Logger(configuration!.logger || {}, logStore) const startTimestamp = parsedArgs.get("start") ? new Date(Date.parse(parsedArgs.get("start")!)).getTime() : undefined const endTimestamp = parsedArgs.get("end") ? new Date(Date.parse(parsedArgs.get("end")!)).getTime() : undefined const numberOfRows = parsedArgs.get("n") ? parseInt(parsedArgs.get("n")!, 10) : undefined @@ -302,7 +302,7 @@ async function main() { logs = logs.slice(-numberOfRows) } if (logs && logs.length > 0) { - const logWithColors = configuration.logger?.colors ?? true + const logWithColors = configuration!.logger?.colors ?? true for (const log of logs) { const { processId, severity, category, timeStamp, text } = log const isStdErr = severity === "error" || category === "stderr" @@ -350,7 +350,7 @@ async function main() { } console.log("") printHeader() - await printStatus(configFile, statusFile) + await printStatus(configFile!, statusFile!) exit(0) } @@ -428,7 +428,7 @@ async function main() { /** * One last check before starting, is there any processes? */ - if (!configuration || configuration?.processes?.length < 1) { + if (!configuration! || configuration?.processes?.length < 1) { console.error("No processes defined, exiting.") exit(1) } @@ -437,12 +437,12 @@ async function main() { * Ready to start pup! */ if (baseArgument !== "foreground") { - console.error("Trying to start pup without 'run' argument, this should not happen. Exiting.") + console.error("Trying to start pup without 'foreground' argument, this should not happen. Exiting.") exit(1) } try { - const pup = await Pup.init(configuration, configFile ?? undefined) + const pup = await Pup.init(configuration!, configFile ?? undefined) // Start the watchdog pup.init() diff --git a/lib/cli/output.ts b/lib/cli/output.ts index 0848adb..5427617 100644 --- a/lib/cli/output.ts +++ b/lib/cli/output.ts @@ -97,7 +97,7 @@ export function createFlagsMessage(externalInstaller: boolean): string { { content: "Upgrade pup", spanStart: 1 }, { separator: "empty" }, { long: "upgrade", description: "Upgrade pup and exit." }, - /* { long: "setup", description: "Install pup and exit." }, Keep setup undocoumented to avoid confusion */ + /* { long: "setup", description: "Install pup and exit." }, Keep setup undocumented to avoid confusion */ { separator: "empty" }, { long: "--channel ", description: "Select channel. stable (default), prerelease or canary." }, { long: "--version ", description: "Install or upgrade to a specific version." }, diff --git a/lib/core/pup.ts b/lib/core/pup.ts index 495798e..1fdbea6 100644 --- a/lib/core/pup.ts +++ b/lib/core/pup.ts @@ -206,7 +206,6 @@ class Pup { */ private watchdog = () => { this.events.emit("watchdog") - // Wrap watchdog operation in a catch to prevent it from ever stopping try { // Loop through all processes, checking if some actions are needed @@ -279,7 +278,6 @@ class Pup { // Exit watchdog if terminating this.watchdog() }, WATCHDOG_INTERVAL_MS) - Deno.unrefTimer(this.watchdogTimer) } } diff --git a/test/cli/args.test.ts b/test/cli/args.test.ts index 814ffec..e8b0dd0 100644 --- a/test/cli/args.test.ts +++ b/test/cli/args.test.ts @@ -1,8 +1,9 @@ import { checkArguments, parseArguments } from "../../lib/cli/args.ts" import { assertEquals, assertThrows } from "@std/assert" -import { spy } from "@std/testing/mock" +/*import { spy } from "@std/testing/mock" import { Application } from "../../application.meta.ts" -import { printHeader, printUsage } from "../../lib/cli/output.ts" +import { printHeader, printUsage } from "../../lib/cli/output.ts"*/ + import { ArgsParser } from "@cross/utils" import { test } from "@cross/test" @@ -22,7 +23,6 @@ test("Boolean options and aliases are parsed correctly", () => { assertEquals(parsedArgs.getBoolean("version"), true) assertEquals(parsedArgs.getBoolean("help"), true) assertEquals(parsedArgs.getBoolean("autostart"), true) - console.log(parsedArgs.getLoose()) assertEquals(parsedArgs.getLoose().includes("init"), true) assertEquals(parsedArgs.getLoose().includes("append"), true) assertEquals(parsedArgs.getLoose().includes("status"), true) @@ -58,8 +58,8 @@ test("String options and aliases are parsed correctly", () => { assertEquals(parsedArgs.getBoolean("dry-run"), true) }) -test("checkArguments should throw error when autostart argument is provided without init, append or --cmd", async () => { - const args = new ArgsParser(["--cron"]) +test("checkArguments should throw error when autostart argument is provided without init, append or --cmd", () => { + const args = new ArgsParser(["--autostart"], { boolean: ["autostart"] }) assertThrows( () => { checkArguments(args) @@ -69,21 +69,20 @@ test("checkArguments should throw error when autostart argument is provided with ) }) -/* -test("checkArguments should throw error when cron argument is provided without init or append", async () => { - const args = new ArgsParser(["cron"]) +test("checkArguments should throw error when cron argument is provided without init or append", () => { + const args = new ArgsParser(["--cron", "* * * * *"]) assertThrows( () => { checkArguments(args) }, Error, - "Argument '--cron' requires 'init', 'append', '--cmd' or '--worker'" + "Argument '--cron' requires 'init', 'append', '--cmd' or '--worker'", ) }) -test("checkArguments should throw error when terminate argument is provided without init or append", async () => { - const args = { _: [], terminate: true } - await assertThrows( +test("checkArguments should throw error when terminate argument is provided without init or append", () => { + const args = new ArgsParser(["--terminate", "* * * * *"]) + assertThrows( () => { checkArguments(args) }, @@ -92,9 +91,9 @@ test("checkArguments should throw error when terminate argument is provided with ) }) -test("checkArguments should throw error when watch argument is provided without init or append", async () => { - const args = { _: [], watch: "path" } - await assertThrows( +test("checkArguments should throw error when watch argument is provided without init or append", () => { + const args = new ArgsParser(["--watch", "path"]) + assertThrows( () => { checkArguments(args) }, @@ -103,9 +102,9 @@ test("checkArguments should throw error when watch argument is provided without ) }) -test("checkArguments should throw error when cmd argument is provided without init, append or run", async () => { - const args = { _: [], cmd: "command" } - await assertThrows( +test("checkArguments should throw error when cmd argument is provided without init, append or run", () => { + const args = new ArgsParser(["--cmd", "command"]) + assertThrows( () => { checkArguments(args) }, @@ -114,9 +113,9 @@ test("checkArguments should throw error when cmd argument is provided without in ) }) -test("checkArguments should throw error when worker argument is provided without init, append or run", async () => { - const args = new ArgsParser(["--worker", "command"]); - await assertThrows( +test("checkArguments should throw error when worker argument is provided without init, append or run", () => { + const args = new ArgsParser(["--worker", "command"]) + assertThrows( () => { checkArguments(args) }, @@ -125,20 +124,20 @@ test("checkArguments should throw error when worker argument is provided without ) }) -test("checkArguments should throw error when init or append argument is provided without cmd", async () => { - const args = new ArgsParser(["init"]); - await assertThrows( +test("checkArguments should throw error when init or append argument is provided without cmd", () => { + const args = new ArgsParser(["init"]) + assertThrows( () => { checkArguments(args) }, Error, - "Arguments 'init' and 'append' requires '--cmd' or '--worker'", + "Arguments 'init', 'append', and 'remove' require '--id'", ) }) -test("checkArguments should throw error when both --cmd and -- is specified", async () => { - const args = { _: [], ["--"]: "hello", cmd: "hello world", init: true, id: "test" } - await assertThrows( +test("checkArguments should throw error when both --cmd and -- is specified", () => { + const args = new ArgsParser(["--cmd", "command", "--", "command"]) + assertThrows( () => { checkArguments(args) }, @@ -147,6 +146,7 @@ test("checkArguments should throw error when both --cmd and -- is specified", as ) }) +/* test("checkArguments should throw error when id argument is missing with init or append argument", async () => { const args = { _: ["init"], cmd: "command" } await assertThrows( diff --git a/test/cli/columns.test.ts b/test/cli/columns.test.ts index 9ec930e..d44f0e0 100644 --- a/test/cli/columns.test.ts +++ b/test/cli/columns.test.ts @@ -1,5 +1,6 @@ import { assertEquals } from "@std/assert" import { Column, Columns, SeparatorRow, TableRow } from "../../lib/cli/columns.ts" +import { test } from "@cross/test" test("Formats and pads Rows and Columns correctly", () => { const columns: Column[] = [ diff --git a/test/cli/configuration.test.ts b/test/cli/configuration.test.ts index 2750aa2..594e53a 100644 --- a/test/cli/configuration.test.ts +++ b/test/cli/configuration.test.ts @@ -1,5 +1,6 @@ import { assertEquals } from "@std/assert" import { generateConfiguration } from "../../lib/core/configuration.ts" +import { test } from "@cross/test" test("Configuration - generateConfiguration creates a basic configuration", () => { const id = "task" diff --git a/test/cli/output.test.ts b/test/cli/output.test.ts index 16d2d22..4985ba9 100644 --- a/test/cli/output.test.ts +++ b/test/cli/output.test.ts @@ -3,6 +3,7 @@ import { createFlagsMessage, createHeaderMessage, createUsageMessage } from "../../lib/cli/output.ts" import { Application } from "../../application.meta.ts" import { assertEquals } from "@std/assert" +import { test } from "@cross/test" test("Should correctly create the header message", () => { const expected = Application.name + " " + Application.version + "\n" + Application.repository @@ -19,11 +20,11 @@ test("Should correctly create the usage message", () => { test("Should correctly create the flags message for external installer", () => { const actual = createFlagsMessage(true) assertEquals(actual.includes("Restart process using IPC"), true) - assertEquals(actual.includes("Install or upgrade pup"), false) + assertEquals(actual.includes("Install or upgrade to "), false) }) test("Should correctly create the flags message for non-external installer", () => { const actual = createFlagsMessage(false) assertEquals(actual.includes("Restart process using IPC"), true) - assertEquals(actual.includes("Install or upgrade pup"), true) + assertEquals(actual.includes("Install or upgrade to "), true) }) diff --git a/test/common/eventemitter.test.ts b/test/common/eventemitter.test.ts index c1c5d13..38f1b87 100644 --- a/test/common/eventemitter.test.ts +++ b/test/common/eventemitter.test.ts @@ -1,5 +1,6 @@ import { EventEmitter } from "../../lib/common/eventemitter.ts" import { assert, assertEquals } from "@std/assert" +import { test } from "@cross/test" test("EventEmitter - Add and trigger event listener", () => { const eventEmitter = new EventEmitter() diff --git a/test/core/loadbalancer.test.ts b/test/core/loadbalancer.test.ts index 6a3b55c..f456ade 100644 --- a/test/core/loadbalancer.test.ts +++ b/test/core/loadbalancer.test.ts @@ -1,6 +1,7 @@ // load_balancer_test.ts import { assertEquals, assertThrows } from "@std/assert" import { Backend, BalancingStrategy, hashCode, LoadBalancer } from "../../lib/core/loadbalancer.ts" +import { test } from "@cross/test" // Define logger callback function const loggerCallback = (severity: string, category: string, text: string) => { diff --git a/test/core/logger.test.ts b/test/core/logger.test.ts index 81f725c..343125b 100644 --- a/test/core/logger.test.ts +++ b/test/core/logger.test.ts @@ -1,6 +1,7 @@ import { assertEquals, assertGreater } from "@std/assert" import { AttachedLogger, LogEventData, Logger } from "../../lib/core/logger.ts" import { ProcessConfiguration } from "../../mod.ts" +import { test } from "@cross/test" test("Logger - Creation with Global Configuration", () => { const globalConfig = { diff --git a/test/core/plugin.test.ts b/test/core/plugin.test.ts index bdea0fc..8d0f7c0 100644 --- a/test/core/plugin.test.ts +++ b/test/core/plugin.test.ts @@ -2,6 +2,7 @@ import { assertEquals, assertThrows } from "@std/assert" import { Pup } from "../../lib/core/pup.ts" import { Plugin, PluginApi } from "../../lib/core/plugin.ts" import { PluginConfiguration } from "../../lib/core/configuration.ts" +import { test } from "@cross/test" const minimalPupConfiguration = { processes: [], @@ -95,7 +96,6 @@ test("PluginApi - Check temporaryStorage path - undefined", async () => { test("PluginApi - Check persistentStorage path - undefined", async () => { const pup = await Pup.init(minimalPupConfiguration) const pluginApi = new PluginApi(pup) - assertEquals( pluginApi.paths.persistentStorage, pup.temporaryStoragePath, @@ -153,7 +153,7 @@ test("PluginApi - Check temporaryStorage path - set", async () => { "Temporary storage path should match Pup's temporaryStoragePath", ) assertEquals( - pup.temporaryStoragePath?.includes("test/core/test-data/.test.json-tmp") || pup.temporaryStoragePath?.includes("test\\core\\test-data\\.test.json-tmp"), + pup.temporaryStoragePath?.includes("test/core/test-data/.pup/test.json-tmp") || pup.temporaryStoragePath?.includes("test\\core\\test-data\\.pup\\test.json-tmp"), true, ) }) @@ -167,9 +167,8 @@ test("PluginApi - Check persistentStorage path - set", async () => { pup.persistentStoragePath, "Persistent storage path should match Pup's persistentStoragePath", ) - assertEquals( - pup.persistentStoragePath?.includes("test/core/test-data/.test.json-data") || pup.persistentStoragePath?.includes("test\\core\\test-data\\.test.json-data"), + pup.persistentStoragePath?.includes("test/core/test-data/.pup/test.json-data") || pup.persistentStoragePath?.includes("test\\core\\test-data\\.pup\\test.json-data"), true, ) }) diff --git a/test/core/status.test.ts b/test/core/status.test.ts index e3d4638..239621a 100644 --- a/test/core/status.test.ts +++ b/test/core/status.test.ts @@ -1,5 +1,6 @@ import { Status } from "../../lib/core/status.ts" import { assertEquals } from "@std/assert" +import { test } from "@cross/test" const TEST_FILE_PATH = "./test_data_Status.jsontest" diff --git a/test/telemetry.test.ts b/test/telemetry.test.ts index 85a8ade..6a72a6e 100644 --- a/test/telemetry.test.ts +++ b/test/telemetry.test.ts @@ -1,6 +1,7 @@ // deno-lint-ignore-file import { assertEquals } from "@std/assert" import { PupTelemetry } from "../telemetry.ts" +import { test } from "@cross/test" test("PupTelemetry - Singleton pattern", () => { const telemetry1 = new PupTelemetry() From 876ef49219cc0ad6eb928b9f6dee9d844c81ab83 Mon Sep 17 00:00:00 2001 From: Hexagon Date: Sat, 13 Apr 2024 22:12:10 +0200 Subject: [PATCH 10/13] Update workflows --- .github/workflows/deno.yaml | 2 +- .github/workflows/pages.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deno.yaml b/.github/workflows/deno.yaml index 1047c54..1737209 100644 --- a/.github/workflows/deno.yaml +++ b/.github/workflows/deno.yaml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - deno-version: [1.38.0, "v1.x"] + deno-version: [1.39.1, "v1.x"] steps: - name: Git Checkout diff --git a/.github/workflows/pages.yaml b/.github/workflows/pages.yaml index 5549cfb..99e4288 100644 --- a/.github/workflows/pages.yaml +++ b/.github/workflows/pages.yaml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Pages uses: actions/configure-pages@v3 @@ -35,7 +35,7 @@ jobs: - name: Install Deno uses: denoland/setup-deno@v1 with: - deno-version: v1.37.1 + deno-version: v1.39.1 - name: Run Lume run: deno task lume From 586919a10be0c26ffa2568a1ddd93aad03a5baf0 Mon Sep 17 00:00:00 2001 From: Hexagon Date: Sat, 13 Apr 2024 23:43:33 +0200 Subject: [PATCH 11/13] Minimum Deno version --- .github/workflows/deno.yaml | 2 +- .github/workflows/pages.yaml | 2 +- application.meta.ts | 2 +- docs/src/changelog.md | 2 ++ 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deno.yaml b/.github/workflows/deno.yaml index 1737209..5566f77 100644 --- a/.github/workflows/deno.yaml +++ b/.github/workflows/deno.yaml @@ -12,7 +12,7 @@ jobs: strategy: matrix: - deno-version: [1.39.1, "v1.x"] + deno-version: [1.42.0, "v1.x"] steps: - name: Git Checkout diff --git a/.github/workflows/pages.yaml b/.github/workflows/pages.yaml index 99e4288..60573cc 100644 --- a/.github/workflows/pages.yaml +++ b/.github/workflows/pages.yaml @@ -35,7 +35,7 @@ jobs: - name: Install Deno uses: denoland/setup-deno@v1 with: - deno-version: v1.39.1 + deno-version: v1.42.0 - name: Run Lume run: deno task lume diff --git a/application.meta.ts b/application.meta.ts index c529f97..38fe21f 100644 --- a/application.meta.ts +++ b/application.meta.ts @@ -25,7 +25,7 @@ const Application = { url: "https://deno.land/x/pup@$VERSION/pup.ts", canary_url: "https://raw.githubusercontent.com/Hexagon/pup/main/pup.ts", deno: null, /* Minimum stable version of Deno required to run Pup (without --unstable-* flags) */ - deno_unstable: "1.38.0", /* Minimum version of Deno required to run Pup (with --unstable-* flags) */ + deno_unstable: "1.42.0", /* Minimum version of Deno required to run Pup (with --unstable-* flags) */ repository: "https://github.com/hexagon/pup", changelog: "https://hexagon.github.io/pup/changelog.html", permissions: [ diff --git a/docs/src/changelog.md b/docs/src/changelog.md index 75c97c7..3314551 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -11,6 +11,8 @@ All notable changes to this project will be documented in this section. ## [1.0.0-rc.15] - 2024-04-11 +- **Minimum Deno Version:** Pup now require Deno version `1.42.0` or later. If you're using an older version, you'll need to upgrade Deno before upgrading Pup. + ### Breaking Changes - [x] change(cli): Rename cli command run to foreground to not be confused with starting as a service From 64378060b838366d6348b510eac1deda4191dd8b Mon Sep 17 00:00:00 2001 From: Hexagon Date: Sun, 14 Apr 2024 00:32:39 +0200 Subject: [PATCH 12/13] Move from deno.land/x to jsr.io --- .github/workflows/jsr.yml | 12 +++++++++ .vscode/settings.json | 2 +- README.md | 2 +- application.meta.ts | 2 +- deno.json | 25 ++++++++++++------- docs/pup.schema.json | 5 ++-- docs/src/_data.json | 10 +++++--- docs/src/changelog.md | 2 ++ .../src/examples/basic-webinterface/README.md | 2 +- docs/src/examples/basic/pup.jsonc | 2 +- docs/src/examples/basic/task.js | 2 +- docs/src/examples/docker/Dockerfile | 2 +- docs/src/examples/plugins/log-interceptor.ts | 2 +- docs/src/index.md | 2 +- docs/src/installation.md | 4 +-- lib/cli/config.ts | 21 ++++++++-------- lib/cli/main.ts | 6 ++--- lib/cli/output.ts | 2 +- lib/cli/status.ts | 21 ++++++++-------- lib/cli/upgrade.ts | 13 +++++----- lib/common/eventemitter.ts | 2 +- lib/core/cluster.ts | 8 +++--- lib/core/logger.ts | 2 +- lib/core/plugin.ts | 12 ++++----- lib/core/process.ts | 10 ++++---- lib/core/pup.ts | 14 ++++++++--- lib/core/runner.ts | 6 ++--- lib/core/status.ts | 4 +-- lib/core/worker.ts | 4 +-- lib/types/runner.ts | 4 +-- plugins/splunk-hec/mod.ts | 2 +- plugins/web-interface/mod.ts | 4 +-- plugins/web-interface/static/bundle.json | 14 +++++------ telemetry.ts | 4 +-- test/cli/columns.test.ts | 2 +- test/core/loadbalancer.test.ts | 2 +- test/core/logger.test.ts | 4 +-- test/core/plugin.test.ts | 2 +- test/core/pup.test.ts | 2 +- test/core/test-data/test_signal_listener.ts | 2 +- versions.json | 14 +++++++++++ 41 files changed, 154 insertions(+), 103 deletions(-) create mode 100644 .github/workflows/jsr.yml diff --git a/.github/workflows/jsr.yml b/.github/workflows/jsr.yml new file mode 100644 index 0000000..0bdfc88 --- /dev/null +++ b/.github/workflows/jsr.yml @@ -0,0 +1,12 @@ +name: Publish to jsr.io +on: + release: + types: [released] + workflow_dispatch: + +jobs: + publish: + permissions: + contents: read + id-token: write + uses: cross-org/workflows/.github/workflows/jsr-publish.yml@main \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 8497413..c0d9059 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,7 @@ "/pup.json", "/pup.jsonc" ], - "url": "https://deno.land/x/pup/docs/pup.schema.json" + "url": "https://jsr.io/@pup/pup/docs/pup.schema.json" } ] } diff --git a/README.md b/README.md index 8a15b7b..5bf59e1 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ _For detailed documentation, visit [pup.56k.guru](https://pup.56k.guru)._ To install Pup, open your terminal and execute the following command: ```bash -deno run -Ar https://deno.land/x/pup/pup.ts setup --channel prerelease +deno run -Ar @pup/pup setup --channel prerelease ``` This command downloads the latest version of Pup and installs it on your system. The `--channel prerelease` option is included as there is no stable version of Pup yet. Read more abour release diff --git a/application.meta.ts b/application.meta.ts index 38fe21f..2aabe2b 100644 --- a/application.meta.ts +++ b/application.meta.ts @@ -22,7 +22,7 @@ const Application = { name: "pup", version: "1.0.0-rc.15", - url: "https://deno.land/x/pup@$VERSION/pup.ts", + url: "https://jsr.io/@pup/pup/@$VERSION/pup.ts", canary_url: "https://raw.githubusercontent.com/Hexagon/pup/main/pup.ts", deno: null, /* Minimum stable version of Deno required to run Pup (without --unstable-* flags) */ deno_unstable: "1.42.0", /* Minimum version of Deno required to run Pup (with --unstable-* flags) */ diff --git a/deno.json b/deno.json index 53e332e..e6e186d 100644 --- a/deno.json +++ b/deno.json @@ -1,4 +1,8 @@ { + "name": "@pup/pup", + "version": "1.0.0-rc.15", + "exports": "./pup.ts", + "fmt": { "lineWidth": 200, "semiColons": false, @@ -6,7 +10,10 @@ }, "lint": { - "exclude": ["plugins/web-interface/static/js", "cov_profile", "docs/_site"] + "exclude": ["plugins/web-interface/static/js", "cov_profile", "docs/_site"], + "rules": { + "exclude": ["verbatim-module-syntax"] + } }, "tasks": { @@ -23,15 +30,15 @@ "@cross/fs": "jsr:@cross/fs@^0.0.9", "@cross/service": "jsr:@cross/service@^1.0.0", "@cross/test": "jsr:@cross/test@^0.0.9", - "@cross/utils": "jsr:@cross/utils@^0.10.0", + "@cross/utils": "jsr:@cross/utils@^0.11.0", "@hexagon/croner": "jsr:@hexagon/croner@^8.0.1", - "@std/assert": "jsr:@std/assert@^0.221.0", - "@std/async": "jsr:@std/async@^0.221.0", - "@std/io": "jsr:@std/io@^0.221.0", - "@std/path": "jsr:@std/path@^0.221.0", - "@std/semver": "jsr:@std/semver@^0.221.0", - "@std/testing": "jsr:@std/testing@^0.221.0", - "@std/uuid": "jsr:@std/uuid@^0.221.0", + "@std/assert": "jsr:@std/assert@^0.222.1", + "@std/async": "jsr:@std/async@^0.222.1", + "@std/io": "jsr:@std/io@^0.222.1", + "@std/path": "jsr:@std/path@^0.222.1", + "@std/semver": "jsr:@std/semver@^0.222.1", + "@std/testing": "jsr:@std/testing@^0.222.1", + "@std/uuid": "jsr:@std/uuid@^0.222.1", "dax-sh": "npm:dax-sh@^0.40.0", "json5": "npm:json5@^2.2.3", "zod": "npm:zod@^3.22.4", diff --git a/docs/pup.schema.json b/docs/pup.schema.json index 4d611e0..3713e88 100644 --- a/docs/pup.schema.json +++ b/docs/pup.schema.json @@ -128,8 +128,9 @@ }, "env": { "type": "object", - "properties": {}, - "additionalProperties": false + "additionalProperties": { + "type": "string" + } }, "cluster": { "type": "object", diff --git a/docs/src/_data.json b/docs/src/_data.json index 15042a8..b58c3f3 100644 --- a/docs/src/_data.json +++ b/docs/src/_data.json @@ -20,9 +20,9 @@ "url": "https://deno.land/x/pup" }, { - "icon": "fab fa-npm", - "title": "NPM Library", - "url": "https://npmjs.com/package/pup" + "icon": "fas fa-cube", + "title": "jsr.io", + "url": "https://jsr.io/@pup/pup" } ], "nav_links": [ @@ -33,6 +33,10 @@ { "title": "Deno.land/x", "url": "https://deno.land/x/pup" + }, + { + "title": "JSR.io", + "url": "https://jsr.io/@pup/pup" } ] } diff --git a/docs/src/changelog.md b/docs/src/changelog.md index 3314551..7ccc99b 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -21,6 +21,7 @@ All notable changes to this project will be documented in this section. - [ ] change(logging): Make `pup logs` streaming by default. - [x] change(config): Support JSON5. - [x] change(core): Move .pup.jsonc-tmp, .pup.jsonc-data into .pup +- [x] change(packaging): Move default installation docs/references from `deno.land/x` to JSR.io ### Non-breaking @@ -29,6 +30,7 @@ All notable changes to this project will be documented in this section. - [x] fix(docs): Docs incorrectly stated that `**/_._` is default for watcher config. `**/*.*` is correct. - [ ] chore(docs): Add a PM2 migration guide. - [x] fix(schema): Expect Record for process.env in json schema. +- [x] change(core): Make most code cross runtime, preparing for Node and Bun support. ## Maintenance diff --git a/docs/src/examples/basic-webinterface/README.md b/docs/src/examples/basic-webinterface/README.md index fe2be81..59e6db8 100644 --- a/docs/src/examples/basic-webinterface/README.md +++ b/docs/src/examples/basic-webinterface/README.md @@ -27,7 +27,7 @@ To activate the web interface plugin, set up the `plugins:`-section of your `pup "processes": [/* ... */], "plugins": [ { - "url": "https://deno.land/x/pup@$VERSION/plugins/web-interface/mod.ts", + "url": "jsr:@pup/pup@$VERSION/plugins/web-interface/mod.ts", "options": { "port": 5000 } diff --git a/docs/src/examples/basic/pup.jsonc b/docs/src/examples/basic/pup.jsonc index 3d2dee9..9591c17 100644 --- a/docs/src/examples/basic/pup.jsonc +++ b/docs/src/examples/basic/pup.jsonc @@ -1,6 +1,6 @@ { // Completely optional: Include $schema to get auto completion in VS Code etc... - "$schema": "https://deno.land/x/pup/docs/pup.schema.json", + "$schema": "https://jsr.io/@pup/pup/docs/pup.schema.json", // Global logger configuration, all options can be ovverridden per process "logger": { diff --git a/docs/src/examples/basic/task.js b/docs/src/examples/basic/task.js index 97e92d5..e71abaa 100644 --- a/docs/src/examples/basic/task.js +++ b/docs/src/examples/basic/task.js @@ -6,7 +6,7 @@ if (Math.random() > 0.9) { throw new Error("Fatal error") } else if (Math.random() > 0.85) { console.error("Not so fatal, but still error") - Deno.exit(1) + exit(1) } else { console.log("Finished!") } diff --git a/docs/src/examples/docker/Dockerfile b/docs/src/examples/docker/Dockerfile index 875592a..6ce24b1 100644 --- a/docs/src/examples/docker/Dockerfile +++ b/docs/src/examples/docker/Dockerfile @@ -5,7 +5,7 @@ RUN mkdir /app COPY . /app/ # Install pup - Pin this url to a specific version in production -RUN ["deno","install","-An","pup", "https://deno.land/x/pup/pup.ts"] +RUN ["deno","install","-An","pup", "jsr:@pup/pup"] # Go! ENTRYPOINT ["sh", "-c", "cd /app && pup run"] \ No newline at end of file diff --git a/docs/src/examples/plugins/log-interceptor.ts b/docs/src/examples/plugins/log-interceptor.ts index a1c0b17..bd7f7d9 100644 --- a/docs/src/examples/plugins/log-interceptor.ts +++ b/docs/src/examples/plugins/log-interceptor.ts @@ -1,4 +1,4 @@ -import { LogEvent, PluginApi, PluginConfiguration, PluginImplementation } from "../../../mod.ts" +import { type LogEvent, type PluginApi, type PluginConfiguration, PluginImplementation } from "../../../mod.ts" export class PupPlugin extends PluginImplementation { constructor(pup: PluginApi, config: PluginConfiguration) { diff --git a/docs/src/index.md b/docs/src/index.md index ffcf3f8..e97f9e7 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -30,7 +30,7 @@ Pup is centered on a single configuration file, `pup.json`, which manages all as To install Pup, open your terminal and execute the following command: ```bash -deno run -Ar https://deno.land/x/pup/pup.ts setup --channel prerelease +deno run -Ar jsr:@pup/pup setup --channel prerelease ``` This command downloads the latest version of Pup and installs it on your system. The `--channel prerelease` option is included as there is no stable version of Pup yet. Read more abour release diff --git a/docs/src/installation.md b/docs/src/installation.md index 22aae2b..26215c7 100644 --- a/docs/src/installation.md +++ b/docs/src/installation.md @@ -13,14 +13,14 @@ This section will guide you through the installation process of Pup. Before proceeding with the installation, ensure that you have the following installed on your system: -- Deno (version `1.38.x` or higher): You can install Deno by following the official Deno [instructions](https://deno.com/manual/getting_started/installation). +- Deno (version `1.42.x` or higher): You can install Deno by following the official Deno [instructions](https://deno.com/manual/getting_started/installation). ## Installing or upgrading Pup To install Pup, open your terminal and execute the following command: ```bash -deno run -Ar https://deno.land/x/pup/pup.ts setup --channel prerelease +deno run -Ar jsr:@pup/pup setup --channel prerelease ``` This command downloads the latest version of Pup and installs it on your system. diff --git a/lib/cli/config.ts b/lib/cli/config.ts index 17e3435..4f55a98 100644 --- a/lib/cli/config.ts +++ b/lib/cli/config.ts @@ -6,12 +6,13 @@ * @license MIT */ -import { Configuration, generateConfiguration, ProcessConfiguration } from "../core/configuration.ts" -import JSON5 from "npm:json5" +import { type Configuration, generateConfiguration, type ProcessConfiguration } from "../core/configuration.ts" +import JSON5 from "json5" import { join } from "@std/path" import { exists } from "@cross/fs" -import { ArgsParser } from "@cross/utils" +import type { ArgsParser } from "@cross/utils" import { toResolvedAbsolutePath } from "../common/utils.ts" +import { exit } from "node:process" /** * Helper which creates a configuration file from command line arguments @@ -39,7 +40,7 @@ export async function createConfigurationFile(configFile: string, checkedArgs: A await Deno.writeTextFile(configFile, JSON.stringify(config, null, 2)) } catch (e) { console.error("Could not create/write configuration file: ", e) - Deno.exit(1) + exit(1) } } @@ -94,7 +95,7 @@ export async function appendConfigurationFile(configFile: string, checkedArgs: A await Deno.writeTextFile(configFile, JSON.stringify(existingConfigurationObject, null, 2)) } catch (e) { console.error(`Could not modify configuration file '${configFile}': `, e.message) - Deno.exit(1) + exit(1) } } @@ -131,7 +132,7 @@ export async function removeFromConfigurationFile(configFile: string, checkedArg await Deno.writeTextFile(configFile, JSON.stringify(existingConfigurationObject, null, 2)) } catch (e) { console.error(`Could not modify configuration file ${configFile}: `, e.message) - Deno.exit(1) + exit(1) } } @@ -157,13 +158,13 @@ export async function findConfigFile(cwd: string, useConfigFile?: boolean, argum // Try to find configuration file, JSON5 first. Take cwd into account. let jsonPath = "./pup.json" - let JSON5Path = "./pup.JSON5" + let jsoncPath = "./pup.jsonc" if (cwd) { jsonPath = join(toResolvedAbsolutePath(cwd), jsonPath) - JSON5Path = join(toResolvedAbsolutePath(cwd), JSON5Path) + jsoncPath = join(toResolvedAbsolutePath(cwd), jsoncPath) } - if (await exists(JSON5Path)) { - return JSON5Path + if (await exists(jsoncPath)) { + return jsoncPath } else { return jsonPath } diff --git a/lib/cli/main.ts b/lib/cli/main.ts index 2f1638c..fb648a6 100644 --- a/lib/cli/main.ts +++ b/lib/cli/main.ts @@ -6,8 +6,8 @@ */ // Import core dependencies -import { InstructionResponse, Pup } from "../core/pup.ts" -import { Configuration, generateConfiguration, validateConfiguration } from "../core/configuration.ts" +import { type InstructionResponse, Pup } from "../core/pup.ts" +import { type Configuration, generateConfiguration, validateConfiguration } from "../core/configuration.ts" import { FileIPC } from "../common/ipc.ts" // Import CLI utilities @@ -275,7 +275,7 @@ async function main() { // Change working directory of pup to whereever the configuration file is, change configFile to only contain file name try { const resolvedPath = path.parse(path.resolve(parsedArgs.get("cwd")!)) - Deno.chdir(resolvedPath.dir) + chdir(resolvedPath.dir) } catch (e) { console.error(`Could not change working directory to path specified by --cwd ${parsedArgs.get("cwd")}, exiting. Message: `, e.message) exit(1) diff --git a/lib/cli/output.ts b/lib/cli/output.ts index 5427617..11de47c 100644 --- a/lib/cli/output.ts +++ b/lib/cli/output.ts @@ -9,7 +9,7 @@ */ import { Application } from "../../application.meta.ts" -import { Column, Columns, TableRow } from "./columns.ts" +import { type Column, Columns, type TableRow } from "./columns.ts" export function createHeaderMessage() { return Application.name + " " + Application.version + "\n" + Application.repository diff --git a/lib/cli/status.ts b/lib/cli/status.ts index d89c390..4100321 100644 --- a/lib/cli/status.ts +++ b/lib/cli/status.ts @@ -6,9 +6,10 @@ * @license MIT */ -import { ProcessInformation, ProcessState } from "../core/process.ts" -import { ApplicationState } from "../core/status.ts" -import { Column, Columns, Row } from "./columns.ts" +import { type ProcessInformation, ProcessState } from "../core/process.ts" +import type { ApplicationState } from "../core/status.ts" +import { type Column, Columns, type Row } from "./columns.ts" +import { exit } from "@cross/utils" /** * Helper which print the status of all running processes, @@ -25,11 +26,11 @@ export async function printStatus(configFile: string, statusFile: string) { status = await getStatus(configFile, statusFile) if (!status) { console.error("\nNo running instance found.\n") - Deno.exit(1) + exit(1) } } catch (e) { console.error(e.message) - Deno.exit(1) + exit(1) } const taskTable: Row[] = [] @@ -38,19 +39,19 @@ export async function printStatus(configFile: string, statusFile: string) { // Add main process taskTable.push({ Id: "Main", - Type: status.type.slice(0, 4) || "N/A", - Status: status.status || "N/A", + Type: status!.type.slice(0, 4) || "N/A", + Status: status!.status || "N/A", Blocked: "N/A", - Started: status.started ? new Date(Date.parse(status.started)).toLocaleString() : "N/A", + Started: status!.started ? new Date(Date.parse(status!.started)).toLocaleString() : "N/A", Exited: "N/A", - RSS: (Math.round(status.memory?.rss / 1024)).toString(10) || "N/A", + RSS: (Math.round(status!.memory?.rss / 1024)).toString(10) || "N/A", Signal: "N/A", }) taskTable.push({ separator: "dashed" }) // Add all processes - for (const taskInfo of Object.values(status.processes)) { + for (const taskInfo of Object.values(status!.processes)) { const currentTask = taskInfo as ProcessInformation taskTable.push({ Id: currentTask.id, diff --git a/lib/cli/upgrade.ts b/lib/cli/upgrade.ts index b511e9f..de2b8d5 100644 --- a/lib/cli/upgrade.ts +++ b/lib/cli/upgrade.ts @@ -10,8 +10,9 @@ import { Application } from "../../application.meta.ts" import { greaterThan, lessThan, parse } from "@std/semver" +import { exit } from "@cross/utils" -const VERSION_INVENTORY_URL = "https://deno.land/x/pup/versions.json" +const VERSION_INVENTORY_URL = "https://jsr.io/@pup/pup/versions.json" const LOCAL_VERSION_INVENTORY_FILE = "./versions.json" type Versions = { @@ -124,7 +125,7 @@ export async function upgrade( if (!answer) { console.log(`\n${freshInstall ? "Installation" : "Upgrade"} cancelled by the user.\n`) - Deno.exit(1) + exit(1) } unstableInstall = true @@ -132,7 +133,7 @@ export async function upgrade( console.log( `\nError: Current Deno version does not meet the requirements of the requested version (${(requestedVersion as Version).version}).\n`, ) - Deno.exit(1) + exit(1) } } } @@ -166,7 +167,7 @@ export async function upgrade( if (allPermissions || !requestedVersion?.default_permissions) { installCmd.push("-A") } else { - installCmd.push(...requestedVersion.default_permissions) + installCmd.push(...requestedVersion!.default_permissions) } if (ignoreCertificateErrorsString && ignoreCertificateErrorsString !== "") { installCmd.push(ignoreCertificateErrorsString) @@ -197,9 +198,9 @@ export async function upgrade( `\nFor any potential changes that might affect your setup in this new version, please review the changelog at ${Application.changelog}\n`, ) } - Deno.exit(0) + exit(0) } else { console.log(`\n${upgradeOrDowngradingAction} failed.\n`) - Deno.exit(1) + exit(1) } } diff --git a/lib/common/eventemitter.ts b/lib/common/eventemitter.ts index af85941..d638ec7 100644 --- a/lib/common/eventemitter.ts +++ b/lib/common/eventemitter.ts @@ -9,7 +9,7 @@ export type EventHandler = (eventData?: t) => void class EventEmitter { // deno-lint-ignore no-explicit-any - listeners = new Map>>() + listeners: Map>> = new Map>>() /** * Registers an event listener for the specified event. diff --git a/lib/core/cluster.ts b/lib/core/cluster.ts index 2ce57c4..3bc4e50 100644 --- a/lib/core/cluster.ts +++ b/lib/core/cluster.ts @@ -7,10 +7,10 @@ * @license MIT */ -import { Process, ProcessInformation, ProcessState } from "./process.ts" -import { LOAD_BALANCER_DEFAULT_VALIDATION_INTERVAL_S, ProcessConfiguration } from "./configuration.ts" -import { Pup } from "./pup.ts" -import { BalancingStrategy, LoadBalancerStartOperation } from "./loadbalancer.ts" +import { Process, type ProcessInformation, ProcessState } from "./process.ts" +import { LOAD_BALANCER_DEFAULT_VALIDATION_INTERVAL_S, type ProcessConfiguration } from "./configuration.ts" +import type { Pup } from "./pup.ts" +import { BalancingStrategy, type LoadBalancerStartOperation } from "./loadbalancer.ts" class Cluster extends Process { public processes: Process[] = [] diff --git a/lib/core/logger.ts b/lib/core/logger.ts index 117503c..1cdcdfd 100644 --- a/lib/core/logger.ts +++ b/lib/core/logger.ts @@ -7,7 +7,7 @@ */ import { stripAnsi } from "@cross/utils" -import { GlobalLoggerConfiguration, KV_SIZE_LIMIT_BYTES, ProcessConfiguration } from "./configuration.ts" +import { type GlobalLoggerConfiguration, KV_SIZE_LIMIT_BYTES, type ProcessConfiguration } from "./configuration.ts" export interface LogEvent { severity: string diff --git a/lib/core/plugin.ts b/lib/core/plugin.ts index 191f824..c3448f8 100644 --- a/lib/core/plugin.ts +++ b/lib/core/plugin.ts @@ -8,11 +8,11 @@ */ import { Application } from "../../application.meta.ts" -import { EventEmitter } from "../common/eventemitter.ts" -import { PluginConfiguration, ProcessLoggerConfiguration } from "./configuration.ts" -import { LogEventData } from "./logger.ts" -import { ProcessState } from "./process.ts" -import { Pup } from "./pup.ts" +import type { EventEmitter } from "../common/eventemitter.ts" +import type { PluginConfiguration, ProcessLoggerConfiguration } from "./configuration.ts" +import type { LogEventData } from "./logger.ts" +import type { ProcessState } from "./process.ts" +import type { Pup } from "./pup.ts" const SUPPORTED_API_VERSIONS = ["1"] @@ -223,7 +223,7 @@ export class PluginImplementation { return false } // Default implemetation of the cleanup function - public async cleanup() { + public async cleanup(): Promise { return await false } } diff --git a/lib/core/process.ts b/lib/core/process.ts index 7353b40..079002c 100644 --- a/lib/core/process.ts +++ b/lib/core/process.ts @@ -5,12 +5,12 @@ * @license MIT */ -import { Pup } from "./pup.ts" +import type { Pup } from "./pup.ts" import { Runner } from "./runner.ts" import { WorkerRunner } from "./worker.ts" -import { ProcessConfiguration } from "./configuration.ts" +import type { ProcessConfiguration } from "./configuration.ts" import { Watcher } from "./watcher.ts" -import { TelemetryData } from "../../telemetry.ts" +import type { TelemetryData } from "../../telemetry.ts" import { Cron } from "@hexagon/croner" import { delay } from "@std/async" @@ -131,11 +131,11 @@ class Process { } } - public isCluster() { + public isCluster(): boolean { return false } - public getConfig() { + public getConfig(): ProcessConfiguration { return this.config } diff --git a/lib/core/pup.ts b/lib/core/pup.ts index 1fdbea6..ab5f925 100644 --- a/lib/core/pup.ts +++ b/lib/core/pup.ts @@ -5,8 +5,16 @@ * @license MIT */ -import { Configuration, DEFAULT_INTERNAL_LOG_HOURS, GlobalLoggerConfiguration, MAINTENANCE_INTERVAL_MS, ProcessConfiguration, validateConfiguration, WATCHDOG_INTERVAL_MS } from "./configuration.ts" -import { FileIPC, IpcValidatedMessage } from "../common/ipc.ts" +import { + type Configuration, + DEFAULT_INTERNAL_LOG_HOURS, + type GlobalLoggerConfiguration, + MAINTENANCE_INTERVAL_MS, + type ProcessConfiguration, + validateConfiguration, + WATCHDOG_INTERVAL_MS, +} from "./configuration.ts" +import { FileIPC, type IpcValidatedMessage } from "../common/ipc.ts" import { Logger } from "./logger.ts" import { Process, ProcessState } from "./process.ts" import { Status } from "./status.ts" @@ -44,7 +52,7 @@ class Pup { public cleanupQueue: string[] = [] - static async init(unvalidatedConfiguration: unknown, configFilePath?: string) { + static async init(unvalidatedConfiguration: unknown, configFilePath?: string): Promise { const temporaryStoragePath: string | undefined = configFilePath ? await toTempPath(configFilePath) : undefined const persistentStoragePath: string | undefined = configFilePath ? await toPersistentPath(configFilePath) : undefined return new Pup(unvalidatedConfiguration, configFilePath, temporaryStoragePath, persistentStoragePath) diff --git a/lib/core/runner.ts b/lib/core/runner.ts index 87a9a72..b3ddebd 100644 --- a/lib/core/runner.ts +++ b/lib/core/runner.ts @@ -5,10 +5,10 @@ * @license MIT */ -import { ProcessConfiguration, Pup } from "./pup.ts" +import type { ProcessConfiguration, Pup } from "./pup.ts" import { readLines, StringReader } from "@std/io" -import { BaseRunner, RunnerCallback, RunnerResult } from "../types/runner.ts" -import { $, CommandChild } from "dax-sh" +import { BaseRunner, type RunnerCallback, type RunnerResult } from "../types/runner.ts" +import { $, type CommandChild } from "dax-sh" /** * Represents a task runner that executes tasks as regular processes. * Extends the BaseRunner class. diff --git a/lib/core/status.ts b/lib/core/status.ts index 6e18743..1f922cf 100644 --- a/lib/core/status.ts +++ b/lib/core/status.ts @@ -6,9 +6,9 @@ */ import { Application } from "../../application.meta.ts" -import { Cluster } from "./cluster.ts" +import type { Cluster } from "./cluster.ts" import { APPLICATION_STATE_WRITE_LIMIT_MS } from "./configuration.ts" -import { Process, ProcessInformation, ProcessState } from "./process.ts" +import { type Process, type ProcessInformation, ProcessState } from "./process.ts" const started = new Date() diff --git a/lib/core/worker.ts b/lib/core/worker.ts index 6f6a0dd..9f23abb 100644 --- a/lib/core/worker.ts +++ b/lib/core/worker.ts @@ -5,10 +5,10 @@ * @license MIT */ -import { ProcessConfiguration, Pup } from "./pup.ts" +import type { ProcessConfiguration, Pup } from "./pup.ts" import { readLines, StringReader } from "@std/io" import { resolve } from "@std/path" -import { BaseRunner, RunnerCallback, RunnerResult } from "../types/runner.ts" +import { BaseRunner, type RunnerCallback, type RunnerResult } from "../types/runner.ts" class WorkerRunner extends BaseRunner { private worker?: Worker diff --git a/lib/types/runner.ts b/lib/types/runner.ts index e0105f4..b4d0819 100644 --- a/lib/types/runner.ts +++ b/lib/types/runner.ts @@ -5,8 +5,8 @@ * @license MIT */ -import { ProcessConfiguration } from "../core/configuration.ts" -import { Pup } from "../core/pup.ts" +import type { ProcessConfiguration } from "../core/configuration.ts" +import type { Pup } from "../core/pup.ts" export type RunnerCallback = (pid?: number) => void diff --git a/plugins/splunk-hec/mod.ts b/plugins/splunk-hec/mod.ts index 6173d4d..8c5e1c6 100644 --- a/plugins/splunk-hec/mod.ts +++ b/plugins/splunk-hec/mod.ts @@ -4,7 +4,7 @@ * @file plugins/splunk-hec/mod.ts */ -import { PluginApi, PluginConfiguration, PluginImplementation } from "../../mod.ts" +import { type PluginApi, type PluginConfiguration, PluginImplementation } from "../../mod.ts" import { HECClient } from "./hec.ts" export class PupPlugin extends PluginImplementation { diff --git a/plugins/web-interface/mod.ts b/plugins/web-interface/mod.ts index c55ca0e..d3e43db 100644 --- a/plugins/web-interface/mod.ts +++ b/plugins/web-interface/mod.ts @@ -6,8 +6,8 @@ // deno-lint-ignore-file no-explicit-any -import { ProcessStateChangedEvent } from "../../lib/core/process.ts" -import { LogEvent, PluginApi, PluginConfiguration, PluginImplementation } from "../../mod.ts" +import type { ProcessStateChangedEvent } from "../../lib/core/process.ts" +import { type LogEvent, type PluginApi, type PluginConfiguration, PluginImplementation } from "../../mod.ts" import { Application, Bundlee, dirname, Router } from "./deps.ts" interface Configuration { diff --git a/plugins/web-interface/static/bundle.json b/plugins/web-interface/static/bundle.json index f661e80..3788232 100644 --- a/plugins/web-interface/static/bundle.json +++ b/plugins/web-interface/static/bundle.json @@ -2,36 +2,36 @@ "static/js/ui.js": { "content": "H4sIAAAAAAAA_6xabXMbtxH-zl-xuarpUSaPaj81mdCuLTmxZmQnE0mdadM0gg5LEvYRuAKgKEbhf-8sgLvDHV8kyvYHiwSwr1jsPlhwdHzcg2O4PgeNBbPIYbKQuRVKGhAyLxZcyCnMmOQFfcgLkX8CvENpzQByjczS8Lur9xeABc7dODFclNxPGcvswqAZAJMcSq1yNIYmCjU1WQ-c-H9MRIFuqchHH81oIbKPpgfHo15PzEulLTwAk0ZcqXd2XgxgovSc2TcrS3z9lysxxwFM0V46gaeqUHoAP3l5NIZX6tJqIacDsGKOr6cK1jDRag5JNpphUaI22UeTNBKDsqdKTsT0XJLRSq8G1fglFphbklINOMn1wog9WYZt5lO0F2pqokUS7VLpT25Zj5mVzOu98O7Ec2kskzmmInw4Y5bV4ulLHx56ALmSxoJZGYvz9zhXenVt2BR_Qp2jtDCGtMUhi1dmVllWwBB2r5hoxD6M4DEex_DXk5N-ZtX34h55-rd-D8CgvcJ7e6qkRWnfrConpsmfPIfh3LEYLkjjYelVTgZwc_Swy6D1n2-ItZhAutPml_DNiXcOAFf5giI1m6J964P2zeqcp0lbg1xJy4REnfSzvGDGfGBzhDEkt9PhLeMwcX-SHsAasDD4mAJ__5IKLJmWdIom9UenyO7NhzF87p4fvH1-26LDuumeAUxYYXAAyQ9vkv4aRtAmeETnAVi9qKldGMT2Xy5ZeUDomyUrrx51Ba36_tEj0PB63jEg-t2HoGtYOAJbbd-784eZfKDmu_a_Vu35u1_rvSUCHldxUVIJSOLa0ZalzLVb0n8aP2WGGgtkhnh2GP3sJ57GqFCMd1nQ2Ou7aTZnZZoWfRi_hCKKpn72UQmZJgNIHtO2XJTd87nT235dpo3xHn4Kb45SDe9QG6Fk1wqa-6efcp-fwG4HpzDcxLvfTRiDxCWcMYtpH4bNl3YQWaYt8idI3xYjQdKITvTJUzwSSjOarhH1RFagnNpZZgM6SR3bqKRnE6XfsnyWpvlCa5Q2YBoXCL6eeHQQxk-Z5t2lVBr6vXWvN_Jw73TG5BTBzpBgGJhclQhWAQNTYi4mIq80IIQGVzNhGizCCqOCSONYXJ8TrcYJme5Gcse_Rncl02wOD8YZuK5Yn3MYwtUMQXBQE0cXCR318N5BpQ4S8qwv1PSSlE5rXr62dqBZZtBGS3o9gAItGCaFFb8jP-c0RHW70enrryMFvxqPIcmVxqSq3REtjOtv57wjBmA0gsuZWgLzaHp_4Q-LNkv-hTA20zhXd5gmM8E5yqQR8LOb8C6n1R5LsqKoTICcae5l-7NSYVoahnGj0P8WqOvgfV0UaVKF6JBYJBRC0KJu4jIajYKytXqLMTH7oXGSsZKzbkx8zXlkn1XuS7W8ZWZkZTUfHYnY2I73b1qqHD1EW-wLSsCWm0z72yRFtjLOdxvqw2l3_rBKFbdMD8NZrnJJUgN-CrW9UbWLw5Ni69qdcOfuwAeWws62-5-jZaLwgeZTw6XgeMt0Gjmz30Dlh89X3Pl2Q-t3guOXOHJbuLftcsbQxGgEpwUy7S60VCy2e0jJYkUVi5bSxS91XEcjuFCM76f1V2fmD8IclES4Xbk_zKdbq8oqg86EcXfPpZBcLb0EIUEjVU0EpTnqzCd04Y7TnH1CMAuNwOTK68E4Rw58QdkaLC2tjBDVJJtY1LHALKRWx2AMbMmEre64cWoEf-83hcgx7WdBr7TfZJNCTV0WYZxfqCl9rQFIVMCcxynRUf1CabXAkP1CUTNg0O2wLyZN_Wg2ILorE8GZuNuTJBJa4tJTWJsJKVG7zscYkiRSjlIWqxVbVUmrq1RcGn-8_Yi5XTuiIVxUpFm85lapAplcwy-CjhQrfoWhk1WbL7SxHXNjLwYybzeF7QzzT5TagnZ_aTb6_AzmzOazUOHDOSxWm3F9ftauoHXpnaJN-1RNCzXN2hV169I-aLQLLXvP25QQe9QfgjHcfFfqUDPGydFDS4V18vLooUaHNEWg7tKyeUm3tAuVswIrMLaGP8DT58ziVOlVM2IoeIVdreElHD00_SnPE-9tf_0f-d2o1PjyJjiptQXbQ6m24sXmdDeB7qJvj72oebqc1QTqO9f1arCVVVSPgFSnLaCKRHnl7Mf3dXOPMo-PAaW3YMOlKAqQyoKQFnWpMSQo4siM6xPuQoUV1wAK669VVoy1yLYxcEI8sftoFdwSH9uCk7WqOytvJXng2MRZonbCPuBUkVctqVpnD_ThJdR9oHqqTn5hpAWjwpiLqKAtjJ1uFVbqRAUpqwrMqDGU3nxQjd4TtZDcl_Hav0cP1cd15tDOYxESCjqcn_mStTBY7S2N7dpfUcF9pcVUSFbA-ZnfR3_uTbPU7X-FG8KyKKdFoFsE3O-wcFmwHF1REPKOFYLTZUGz3KI23uiF5KgN4fkehHQDgmfak6ajX_7Lhr-_Hv77ZPjN8Ldvfx1NB5D8lsSFJ0JFxiOBBhUFEERHZvtF6jMuQ51OcIVCOtef1k2mirLRCH7AGvlTdEzE1EEK35sHzizbckXw68Y72uAuaXeAaIved8Mb-k53vEvfgZ0dbe9YsUAP6PbduCuk7YiGuZrPmaR-SsuELJ9z-OOPziC13tEd2kNEsIVVrqWwIaSegVeQ_AtNAt9C8kGFC84BRmjXAelYoJUkE5IPo9cHc9S4XeMw7viyYslW5mDWFvVcSGZxg3k982y18yXtZH3b9OGUWZfbCGU19_buVJYveR9e7ZuGF5BAWg_1abc6Liceje47wjWcqMPC1RMN7aqM_FZpuSq9y67lJ6mWdBk5jOttofJPyDcYh_HnRmfg7v8k2x_aOlvl1_YPFRHCMrr3Bn7VBLnn5BmKUx9wg2kYh1fVI2FaI0X6LysZXVe20vT7fefFQ2I7aIP3YpsyfvggXTzJZ6ji68umLmH8IGUCzWdoU5_IzYNRH_xXkJwqKX1b5YmCArTakOe73skgzD8n2XgWlG_i3vr-1dRm92Fc6e81-CwrhjNk5dA9k30JgwIHOMww0uHaoCu4J32XZkeQwItDefg3quak078v6Cq8t6jll3HUYQ6qJG_f_v0trMqWADz39_We2nvbx7TdFWtfFUIlZK2ebH15gVP6rQiCsARShQWu0LibIis0Mr4CvCcJO_oi0XsIDKti44Br5wpBt5AzcRcsWtdLW8psQ9Tx68mOH1Q8VOV9dDyogOnxCNYNxiWK5hl210uB55IJj3oDPFfa_5wGuz1t6myUX6aPTdeDr1rdeh8LO9h7fYKENOHibvMZIBNEslfyBgnj_C3dAiikUKJOE_ezIkJ3rYtv56mn00neeCUQuZLRNYS-7rFGBFtoWTfCJ6yCvq7vHyGxMb0H1d3oHcTDfHGLgUHzw5Aul7xYGEtd531cKj1a7itLlPx0JgqeEmnbC1X0WGELjNzhvu_xhylZBS7d0l2vGG6ytbDdl-i8rW3o5f0QKRYjx6fpF1PsUjMA08cJbhmfkj2RQbGDY9KwaNdeOOL2ZhBEaqXD0Ao--_F95IH6BWJfp7N1wqI3izpM67HqVDv2fqilZ5wA_DmK60KnfZScMklZeiIkjxQNmdS9GTvWG6Zm1VNe9E6y9bLU2xEIsZtbTbboaTLa5dYet0Py5uhh6_XEtO4lLlFtRkr1m6u2SPBh09sbWze30-HRQ_sniRtC-4_xcE82T-HjfXyJFjwN5LQQONL-0FOOkpuu33ijDfa2MvoN8Q7tsoiAin_oqj6Eh4ZB55eK1feoulZDoYUF697_AQAA__8DAKGpwdyEKgAA", "contentType": "application/javascript; charset=UTF-8", - "lastModified": 1712443989795 + "lastModified": 1712948338199 }, "static/js/helpers.js": { "content": "H4sIAAAAAAAA_6xY62_bOBL_7r9iTuht5FbxI69207rdOH0F2bRF4mIP8AYIbY0tNhKpI6k6udT_-2FI6mHHaXPA-YMsjebxm-HMcKju06cteAofMc1RwawQU8Ol0DCTChSKGBUXc_h6AkzERMyYMUSJmWGdFljhP2Y8RdCGGT7tftPdxCrTnW-6BU-7rVbX2TiW4jsqo-Ho08UJTGUqFUxljBq4AIM3BoyEqVQKdS5FTFY-js7-JCvwl2K5djIYg0aPUs6cIBfwSudMvAbD5hoW3CTA8lzJXHFmCNttitpqOsc8ZVPUkHKBMFHIrr3Aq4l6bf0mqxBznafstvIxZ4plcKcNBWQJTGg-ItPbYP-mUhjGBYFed8-a_UOhKZTQtQZrxT04-82IKIcyhsltA3y3hTe5VKZaJwdDfjRZGpaI2nDXAsKjjX19TGqPrdYBjFsAAHfWyiHs9iJn9RCCScqm1wEso1WOfs2hML7_fqd-P1eI4j7Hbs1xi2kqF_dZ9powCrzPsF8zZGyOwrD7PAc1z_SWbQDyvGZYJNyUZi5bLYAUDSQmS2FQrWzHr0HY_fumP_x7HI57279f3vWjnWX4sr5vv2m_GWc_Ti-78wjCjJlpEkHeb8PgtV0IAD6D8B9EcSkAwauuzdXAvnUL5VZ9AHm_o_OUmzB4GbTte0KmUBepgQEENstdRgy2SgWUYzOp3rFpEob02DDuzBMRBoMBBP3A5Yf7ecXPBhDMpDDbC-TzxBxOZBq_dNoBloCpxoZQiZiydbCWYp0ZF3EYljGfli9gaTHlTGk8EaZEORjULM7dGnEqVRNqE-yVW8cnd_a_Y6_Ll1cV77LV_F86zQ1ft3zs_YK4N62Ks7HyglY1oM4QtClPvMSV6zZP7ihnln49r1rL-73OJAhz_h0FNS0pYmp2RgJbqXwGhmcIheDGNotR4h40FBpjYArhFpnSEcTsVkeQyIIeMi4KgzqyvdmrX-9XosgmqJaV9W2r3FGpfZZ023ot5gfaFYmVzJ4TYxIjD5u9loCHuh2BTuSCXGQGMqkNmIWEFL9jWoPEG5blKdJ9t-uDqyHoW3cj2LH-BvTabT0jnmG429_f_f2g12u__IWaXR-uHRewdT07B7u9R2nx4d7vlRFf19TffeEV3e_QDTYfvmaDdqtc9uVxQH4HEZCLB71e79K1sHEQs9sgghcHew0awSLegwbNAQwiOKhpzmwQQf9ypd9VXWV82QK78YVEHguWYQTfWVrgJaWIxViWItVmmQavB46rLlOSJ_ajTBaCVJ8xk3RmqZSqkup6IS_iUHTyQifh1ZO7WnoJzSd4DX14A4QNnkGgAzi0D8urUlGp_58elav9VoXaG0pRzE1iW89O2-3_VPmN4vaM3yQXYRBB0P55XU9uDT5Q1bagN1XzkGQiOB1GcDaM4MPQlvBoWFW_qzBNCcQ1ZCzGstR0xtIUferYVVskfJrYd9ZvEpgrZAYVmIQJkArw3wVLSUP_of7gvFjvDo663hu86ETKFJlYwjhXXJivgpuBUZQ02_BXgiYhHQqEtIMdF9O0IDd8NOzQl6APOg11NnQrFsreM84VzlApjMkI6f9SEuowkDJZmLwwEG7ZAG9FsHU6pOuZvX6w19Fwq_2THudc_mWHi0DJQsSOYQdinPKMpeBmy8glAK0NN4ksDNjV9-39Z83m9-f7cDps9BfrSNjv2d8ve1XPZdY9-V9L7nUOnsOH-5b3neXeL_qbY7axi6BKBxgAJQRRGuvn2gWVpIs1lWKvno4qH6omeQ0D6Pd29iqC5v9x02zgWCMITod0PbPXD_Y6GgbU16gl8dVOZG9TOXf229CFinLdtts8gVvBDL_95qx2fB7r1fftqj_CoGKM8ebzbI2v7DXOkaoF28HofSqZCX1UPKhcLsLrCHi73THyPb_BONxpkxYfrjrWb-DqyZ1TSK3Tghjzy-UVHDbebBxSGORKTlFre4grKP1dw2GQFBkT2wpZzCb2kFcW6cYhw0lvw0VDjZzV-h86Cq0aWQWz-eDzxfGQIRzJC6sodALN_bXJBgO_Rsfn745G794eQs9tkRejo_PRyacPh9DvedL510-fLGWnpFyMPn_54kj7nvT-5NPJxUdStFtyvTs__3xOlL2K8q-PR18vrLm9UnD45-fjU6LsO65GRjgXzlie0z5SIh43_eh4_JeHEBzbRh8H0Qa20i3iuzBM0bl9I6N3lvjOC0Fn2Af0uQg4fdIi3MhYxoUY33PBdfIAQh8t4nunFB3tN7OVIbSMNwkr9EM--8gS4zCV02vP1tzeVyI8dk-X8OMHBF_FtZALETRK5C0aVBkXSEXijjw8RmH4jKOCCaMNnY7hG3L2_1kgx-uWw2AuJc10wYTZvwVTbuEgEFgYxdKgvbly5micdat0pWb0gptpAuHPiqtsdFOmsc6Xw2qac03comvwlct72KRVK7kuTT41GKtka0qXy9ukVVm-rrCMjpsGY5yxIjXrTGXcaF9861jcktsEarRNlk6LlBoKDQYxn81QoZgiTNAsECkb3GQYEw8NdsQ3LZRCYSpivdGuq2GUa5sb7VtmcOlUuFHN3tr5LMtpsLRzx7q5n0w7_oteNX7RhyeFmvJMzK0iezKt0VUT6trcBguepjDBcqzzrm2N_TliTNPPJbC53KLdtRqfSZ11grvPgAlCzrSJaHhaF54pmYGQi1UNDXFSZo0XplAYwSJBhVBq4W5qF0WGik9ZuhJzEZdWHBupWp_57OdGCke45Q8bNE_6MyHd2oPilsNOR8-tB0qQlBzNZUjIm7uVkAsYgMAF0EKHbT--eGNfmKZRIRRy0ZmjO1K2YdstcUWgcYbGxVK2mM34DX06otjbAmgc4azKVzR9uaJetbTdeHSva2XlWgRld72v9aBSS0Dc2WRQfwJqMJcfaGwdOsZnEHqJwcCe_AIPjg5-5b0O2vDMo3oYBx2QH4sEugT7UXD8UdvCKb8LPAbOCzrE_w94LPxHIbIfBCwe973jPppHmHPw6u3yQWv0ScIas99oGraWrf8CAAD__wMAjoXGR1UYAAA", "contentType": "application/javascript; charset=UTF-8", - "lastModified": 1712443989795 + "lastModified": 1712948338195 }, "static/js/state.js": { "content": "H4sIAAAAAAAA_4yQwWrDMBBE7_sVc7QNtb8gUOgph0CgX6DIK0dBrIx21bSU_HupkuZQWtrjDm9mh5mGgTBg58QtrLAjY0n54BLUnDFCLk088-EhinEJzjPWVJcoyAH7uo6ElvEYYuJmi3466dQCxpMShonIJ6eKfcmeVZ85sbdc8E6Az6JW6ufd9U0B7Bh11EbxfDNtZ2xQZeYQhWcCLgQoWxfnP23xi1_Y7k8KWy3yi6nxFyJ-XXOxa0mst_rmrOpWXlgslzdsIHzGzq1d_yP_lCXE5f_8fZ4r-G20rqcPAAAA__8DAAR4LUm2AQAA", "contentType": "application/javascript; charset=UTF-8", - "lastModified": 1712443989795 + "lastModified": 1712948338195 }, "static/js/main.js": { "content": "H4sIAAAAAAAA_7RVTW_bRhC981cMeKIMWlSuBVoEtRNAQYwUcHIuRrtDcezVDrE7lKEE-u_FLklFcos6ReOTxPl4-3be7ExzdVXAFdwheyCv4QC9sFdoJYB2BMYxeb2ObAk-4B7vTeBeAfvesUFl8cuU_7njCC07Ao4QKPbiI28cZZyNiEYN2Pfstxn1LL2GltR0ycOeldGBRcU6oW7JU0BNvj6IoRjBYLAR0Fvo0FuXXF_WwF4poEl4MfFJyTtkXy3giZ2DMHjAVmm80-2nO-gwQjs4dwAnaMlOSW_zHaKismkeYpNAlg-xgKumKHjXS1D4BqZDv6WPsr030lMNQ29Rae2jojen7z9GyjcYLByhDbKDctkMvHyI5RmWeA3ipuCpGt-h8ufkpHiG40mfJDxegk1VuidHRiWchacrUQ4umlHy9Vht_krxuSZZ0vfp5Mk34VIc8ZIpUthTqGeNaJYmCU5oujknC5n0QlWcASNN0SqzIkv43BG0HKKexOYIMV-FLGwOYKnFweks8FuMB29G1Qafxc86ZTPMprkPvhUAqb3TL0DTwI14T0ZhM6iKz1SmDsoRVsywI6_LLek7R-nv74e1rcqoGPR6olgulmjtuz15_chRUy2q0jg2j2UNI5FqAb_-9kzm6plQ6ZBqUcMIXi4WL1GQ_vUYSP8igY0T8_haDDL4ixQG_6okJvgXaQR61X6Y4BONf-ehIm6D4Rqdm7nQj7MZH8X4LPJsmx_m1skGHcQ059IT7DHGNHO9aBrZU9rlPKzGkh1_lLKRQP-5fj-XcZk4lJe8mwa-5DkOPE1jYN9K2OURCbSncIA3K-0gkhFv6zzleLcjy6jkDhklkq7Tctqjq_6J_-XqqPAJWS93QOqDM_NpF1RTax5reLP6c7Vafed9K4D-tE7HI7Lv_552BINqOqgoBAnjUIXUy1EcLbOxKt8jO7JJj4kBf6XnG-aXsoYRI6EWx6I4tcnf9b_9dHcjXpMt7-qyzkN9UfwFAAD__wMAhBQwb70IAAA", "contentType": "application/javascript; charset=UTF-8", - "lastModified": 1712443989795 + "lastModified": 1712948338195 }, "static/js/network.js": { "content": "H4sIAAAAAAAA_-RZS3PbRhK-41e0UaqYdGhg98paxnE53o1S8lplyZuDyxUOgSY5EjiDzAzEcBn8962eFx6iFDnZ1B5WFwGDfk0_vu4Z5i9eJPAC3jHBNqjBbBEEmr1Ut6Dw5wa10cBECT_i6koWt2igkLtdI3jBDJcC1lJZpj2uXnJhUK1ZgVBXzYYLkGu4bOosAavj2zWvELRhhhf5jc69nuxGJ_AiTxK-q6UycARWlhdyM4OmLpnBSyUL1PoNU2VYuuIlrpiCFtZK7iDN8oZnNzrtRNSeSYo135yLOxRGqsMsrF9hhYWRqlswzDQ6EvYkk71ohSf4i5W-bkRh975BgYoZjL75-OFiMoVjAlBIoQ1UsoAF7Lko5T6rpHNZ_ForaWQhK1gQYda9LhaQbo2p9TyFV5DuNT3M6WGedtxkiuekx1ewnJ8dw2u7JIaOuuQqEDOzFWyHWcW0ORcl_vJ-PUnzdArPFgt4-Vd4NSTTzUobxcVm8pfZ4wKm8DWkubU0J80KTaMELM-OYWttnjsTt1IbktGeHa21Z8eSq3avl0mbJHkO54Ibzir-bxwknhBoPZ-4TWm3vgCB-45ucjosUyv4dVkCUpCh4toQoc1gLgq542IDO9SaCiFxsjNWlm-J_MJTT1JPkc5gYgVNYfFNL-T-Myzgh6v3_8xqpjQ6wqxkhk0TgC0TZdVZ985xTDznNGnJVFeW31tSfd8-l55RBpUYfFszxXZwlKsbLEwbTXkJ11uMbwoL5HdYOglUugMpeRLT-zfstJvOc3grdKM6-VxDiWsusLS4sWUaBFKJMXWglK9RGY46AeBrmDwLbL_-CuE5M4d6uGA9Z_U5N8sKM1RKqkl6Lu5Yxcugfp7OwiO5OiRhAtAmCYDec1NsYdLXFAUzjZB6PPiJyr7RPxVbJjZYpnNLcmK_1rbMUdsNn1jPeAlMYfCLF9Xf_kDKeOedlGDqI37w9luYbXTPHX1RzjX0t1LIbv2b9RD9ncbETKOZnLbrASVe3D0YH0iJZFT2a5uQjqHbC9dQNEqhMNUBtIVujL3AchgpK2oITMMeq6rn4RHgZxs0k6mF2N928aDZPLD14Mo2GTvU5VMlNzF5XF8b7X7MVuKaNZUJPKMwfxS3Qu5FrDZK4H6QbULbbG8jiLyRwihZuebu_YEaVuRLURKqsNDrwUhgoGss-JoXQMXq2jyKspZcDIHG9YUWeOkx5vw7avk9NSSvcOpPcnYKnIDu3UioUa2l2gG9dSKdHFfWGo6XSu64xr850PumhddESEtgtsyAQi2rO5pspJWiUd2houVaCo1OmtkquddwfEtObuHHLQpoBFtV2LfDDAykrrEGJoD7yus-cR1ciKXDVD85MH0QRTc_eM_4yphQHUUhEV5fK8UO5NaRFh1bjv3wPq7DAj6l2jBl0hmk2sia_q8qWdzSQyPio0JH9plKMM_hXySISioqCSg9UpFxUVRNiXrS2RsKx_rSdmTrzckyIFMkncPZMb60GbxrtIEVghRI-zw7jrXdSC4mZPC0zZYuuxOACk0MYwJg1MFbEBZhAWzPuIE1mmI7WWZ5X29-duRlu5zFet-h2cpyDuk_3l6nM7-4RVai0vMeKqRUTyjMy-tDjekcUlbXlR-H8xstRWRu3UNrTYaC2dZjC_l0M1taj4V8o8LsG9zPPRELzO5ivpyBk9sLgV0IzrJRjEkvbx-M1vfX15eO9VnoIXB2jJxuqe2i4FJQoW4qEx0eyckdk6nX74m--sqTZ7opaA_BFge350IbJgo8F2tpWdtulHR8PWj7O0XWzkeOCQhTu8nGFbstcVt6j0HHp8-Pg0dU4bs0aXoCeNjcGyKvg4QRFli6sHd_iPjSDA-nlfSpCZe6hLPclG7RRGoppxOKcEKqEBAmAHe1OQBzKKVC-yJQavBPz75gxsmsu58mFIZKbrSd-Xt9LhTTKHEIjik4rKoOsOaVQepa1DgZGL5DUDQczkDjHSpuDlDhHVYzOwbSxiq-475jduk36oBe9fmjLdQlUbD8ZCf9ZKH8mu_wyrBd_RlewmsR7bfmavoAXJQWrMTG7ZTYgspuU9ZD-y2n3O3rP60bRfnlmlGUf1RvcPxY5zAgFLBOpA1jzITTcsUHuddjoTaaYRgRzW6Fiuyv5IaC3cUp-yMoY-3i2kaks--pEBM4Hho4Nmgu5EaHofi8nLn4x-DNKCq9t-DIGViXxKGkdz73obTTIey5GYAdnH_ncSySLGCZ5WTo0k8dlwprOh0xAc5H8U7p5wbpzEjBQYNKe1F2gcaco8UAf6p_Hjf1PDjSE_I1cPM8Hks9HkXyAEaOOusKctEVZ8CboGvotacpHPKMtA4_wmIUl7H-fpSepr3PMdLd_wSLQQbc27fPhyfu2FOP9xqqcxHza6zHZtvTlPQSM2qwa3QvRP97st9IcYfK9IX6lLPnno8fLq6QqWJ7afHAffJJZ3PRrlvBuB9R64kTOA3bqGsCuFNpHLYVSiKBrjq-XsDy1dmxpy0z8srdv01bWzJfOhUE2V84hcahIDbMbuSMdfG_nDx_T-_3yNodgv-UUfGkquy_PSz6YyPq3z8tRi3_PxNjvHr3CfDQkSUBm_OTAf0goMGkXjPJCvt7Ax1xhiuDi6X65G8T9nrtHlf8gcIvOe-6y6a-Yn8o6Sk-caP1yL3ePa5Ocf_KkBT3zmPRF1RvfuQ4Do8ys1GyQpv8BwAA__8DAF3ENapzGgAA", "contentType": "application/javascript; charset=UTF-8", - "lastModified": 1712443989795 + "lastModified": 1712948338195 }, "static/web-interface.html": { "content": "H4sIAAAAAAAA_8yZXW_bNhSG7_MrWO5mQycz2ReGQRLgOW5aLKudyG6x3VHikcyEIlWSsuP--kFfsWzHrpQ6Q69kUnzPeXhIioe0--pyMpr9Mx2jhU2Ff-YWDySoTDwMEvtnCLkLoKz4gZCbgqUoWlBtwHp4Pnvj_I7bryRNwcNLDqtMaYtRpKQFaT284swuPAZLHoFTFn5EXHLLqXBMRAV4F4PzxpTlVoA_zTOXVD-rasHlPdIgPGzsWoBZAFiMFhpiD0fGkBWEDpcWdEwjGETG4A7ChbWZ-YOQiMk7M4iEylksqIZBpFJC7-gDETw0JFbSOnQFRqVAfhv8PDgnhUsqxCDlsnSGCt-J5nbtYbOgv1785ASfhX74OBdZnH--eZ1F85EyD1Eibm7fDz-Ry3_vPpjRuRj_cv5ghpcmhhs1Xmfj1yBX0YRf3d2TMMivrv56O17fBerm4vMHbqi88TyMIq2MUZonXHqYSiXXqcoNRhpi0Bp0pgSP1h6WymmqMCLlcJJmPN1QsTWKBDXGw4zqexQmTvmMqyf2z8oAIuS-chw0zTOklqCL4UWOUwUXIZcazqCxs2sCcebhsoXTaOthKbUGIsuVbKlTYDxPC339yyolQqpbqmJOXvjT-dQli4uWMVJb8xFCey7acuRmjcc4cSTkVlOB_eH1NZreTkbjIBgHLsmeMr2PXnQw0yoCY5yIauYUk55yCTvIZQirdqhoZ9CKC4FCQAlI0NQCQwvQ0Aptd8cMLOXC4KZbC85YsX43cTjU6brDaDR5_-bd1fx2OHs3eb_V-WJF0rBZhps6vV1RVLHGg7FayQT7I5WmVDKXWPZU663YKRnzxIkqBfb3NS7Z9dkN4qPS91wmiHHdB2R1UohhbpWxVNseCLTRnBLkFvpiaDg5xEgr2WcstJKndo8s6JRLaqEHyKOmC41L9tbN8TUYzIazeXCyxTdbZ536Ziy1uXHsOoMXmGimB0I908wpMf4UKroH1oMirBSnhAhKyz0YqseJEbTtFYdyLE4bh_ED78cApeCUCPOM0X4MeaU4JcQMBKRg9brP-mw0pwT5G1Kl1yg3NAH0_W0Q_NCDKC3FL4fzFmiGcgOMWGWp6I_mLIBmTil-OcrxgwUtn4UHtfRZu8mR3LDDljOffu1248-KuDY5LRz6uvmuyWidreaZUwcCNrlqE1WXFA0PRaJd0QWuPUbdyOoxKRUvDDfPLE87YuVlW4xelugSpEJL0IYfTMx2uBhI5dSKFw5XcfLsxfY8rF6LzSXlqba5_aB884bx5ZEDbfv422r55DG39X6zeJGlIfavJ1eBSxhf7gmKINTWHCrEEysuTpz6fF7Zcml9G_Id7qaubgeGQqDH1y6h_pd5IqWhMdkb55C4ohkpDR0hcq1B2j1T1aEZbbMctEbL6WBax_3dw_f2DAtza-tTe5lY7bkPE0fwZGFRqDQDXRWw7_LHrlKDYupkgq4LyESpMifhvksq48c8quzZDgtx4TCknf3VifyzXWpgqnCZaZ7SKsXo4rbM3J_t1GoaxzyqG_frcC5P7Pv46O5My63idqGZrkIlW1TFFC-uqFqtXbL5jJXXVcHaWEjROxkr9KF96-fGSlnQx67t6v6Gylr1WGqcnh24_qtjBLHdVjQlq7Ldz2c5u0vOZgN_-v5tulgbHlGBqsSgHcvHrWPbTpkIOBnoCKQ9tJV0tfNF_c4Q7u42nSN1OFRt1mBFs2PoZkWzrw_Axsr_1v1OvZ8f7XuTcH1LxJPgGLEqbk4EUPONUV8rytBwCZomcIxfKMq-itwl1QepLplI88wioyMP35nyqza4MxgVN1weThXLRXnTVbUrRC4p_gzxz1xS_g_2HwAAAP__AwCFH_NOFxsAAA", "contentType": "text/html; charset=UTF-8", - "lastModified": 1712443989795 + "lastModified": 1712948338199 }, "static/css/web-interface.css": { "content": "H4sIAAAAAAAA_5RYa2_buBL97l_Bm-ICbWA5svyIqwAX2Hbv7r3A7n7p9gdQEmVzI5ECSTfJBv3vCz7Elyi7SdA64pwZcQ7njEa-uwULAD7TvqcEQNIAcUI9At8gw7DqEAeL27tFySgV4HWxACDLWkpE1sIedy8lyOAwdCjjL1ygfvmpw-Txd1h_UZe_UCKWN1_QkSLw9f83y5s_qKDgCyT8Zvk_1H1DAtdw-RPDsFtySHjGEcPt8uYnGRJ8ph1l4L89_QvfuChm4cHthOO_UQny1f2Oof5BbvHuFvwM2eMkk9s75VUdswayR8TURwne5Zt8m28ffONoWhfrj-vamnrU4HNvjXVRbzbW2OHjSYy2ot42-9zajpQ21g1tt0VlTRW0lu1uvV0jaxkY7iF7cX5Fs3XbJOgsGOzsDYvisFlb6xNkBJOjjbzdFmPkNs6_hW2LisA4Ywrzr2q0bXfWGOTfyh93Qz__fd4cDtCavPxrtKl3H60lyn9XHQ61OYt2kv_usN_dO98o_6ZqdrtGW2tV61lFWYNYVssyMzB2rN4fiiX4uF6CdZ5_GKvpN5nZ9XJSBMjcC_kb1tNoQ7n8jQtqtNZ541s1o6MRFnXRuLCK0tHWfGzvGxjUlN0NbHbNblJUNuqhzceD8qrKBlY_k7KywVu0rV0JhERIOjeHJdjul2C3_RAW2BVMSMwcKiBIgvaHJbjfL8Fh70ABUb74TPGNJl99XvU5Tyc_r_xGs68_r_5scE-AqQL0clgXhyVYb_ZLsN7KXL8vFne3CwAA-FNVIEcdqgWmRHbmijYvK0koeNW1-pVjcgRq5VrBltL2Pm6IhjizGGEihD4lD-M1CIcyuVmQaxQOIw_Jg9h-4RAV9AFj13B2c14exu8fDmcOzsP5ncThzAl6OL-nhNXsuAyba4iKMBEi5DJstlHFe6AJl6bgPUjMpS57DxBx6WrfwyS4dCLwcAkunRo8XILLhCxGh7mWrdShJKB4CDWgl94uAuV3WQURZE4GESytgwiUEkIEmSohAsxLIQLOayECzovBB15QQwIWyyGCzOkhgqUFEYFSioggU0lEgHlNRMB5UUTAeVX4wDfKYnRVTw052v9M63OPiABPuEHgDnymREBMEOPgDvyKCGK4BqhDEsPlI2V1wk2DCHgFoMF86OBLCQglCPwL9wNlAhLxAL4vFisuGCVHiVOD-BPSR1HRrtEAe_zgFQQbt4YPEjjiZlAWowsggdIGizO9YAIz1BiULIIESC5bTAVTkAo6hKmBBMpYLNIUQQJpLBZpqiCBNBaFXKxsywKvoIL145HRM2nCErEYHd1cXnewcEv6BQef_7GvXfbwjsI0uct4dyq6411G2wNyne-yQ3BWrgtedgqOzXXEy07hCZ6HQZ2eQM8iEwwS3lLWl2q5hhw9hMoSJ0yUIxx9GlRTBuUwWEqJSiMsT_Sbi5pAqCemelYGr_KmytySakDeO7YHkAvK3GGCspPZ33qlXgV6yI6YlEC9yFhbnn87yQXbU9oOPas-1VIq1IbB1Axk2xIn5f9veTlQjlU2oMXPSL2ZVFQI2pcgH4J4_wFcz8k61Q49l0AN6ANsGtV389VOfmUQJGm_SEhs5a8zF7h9yWpKBCKiBDUiAjFpgh0-kgwL1HO3PE7uX2TnHSBBsr1CLq9eU_HlZ9ZgprddSvmfe6IoxSTTRGxylaZk-dksbccl5X9k9MkkKuug7ehT9lICeBZU7kjfPuTGEJJ1qBXl2mSvTzEz5OarQtMyRjgVytWgBB0c5ILvamC0RpxnNWSNf29JvHUP9gOiDTFdT_m4apkBlpqrxDL6NHto8e5Bvhp38ANNdvF98c7PUdWKetxOmc4njGT63Q5pan6kYU04xf6xaK7y1SZiX2DR6RqcPrknEe0VF1Cc-bzb3S34HWKiKh28_5PSroJMfafY0SP_IEfvxTv556wgNdHfR9jA9CZtQ1kVxt7L-0RR3iCnUCiyEwuz29fZ8hsb2SbZHeTrua2oRH9Qd3xicCiB_D-86UrAanpsYD3Tht50k3dQZc-jQrze21QwRJoLQpnQqLrdJ9gcEZezK_jKEQf2CS8rwU5e8sIMruq6pUyPO5gc1RRayTBaBxo2Pgi0xs0iFy8dKgGnHVY7tXlhoh5NVUfrx7Dpo171GvWR7-yVF5TBBp-5Uv7YkoInxD7sSFrMqxFqtMqOFXxf7HbL8V--uve-zfmkbqUHbn3bN-Yqhw69Lu_vO8tr2xODCMoyCYNJg2soKMt-IOD6BwIao6CDH0XQIR1EGq5vaj5acT2asemW7ofRK-l9GdtcMDd7J17N4rnbuVx2COBu9k47BHO3OZBx9k57-HO3J740PngbGjmBF9Bu7tZ2b_ZOO4Rzt8Z4s3faKZy7jV7c7J128udurz6j9OXKVQokKKAh5RVTITETOlKOKUokbkJLypkkqFFCielJOQevJne34NNZCErUN2aV-tPTjf5GINky4ylOjdkM9fZBWp8ZlzceKLYPEtddR1RiwpiMNeNY-Q8AAAD__wMA9KeNLsQcAAA", "contentType": "text/css; charset=UTF-8", - "lastModified": 1712443989795 + "lastModified": 1712948338195 } } diff --git a/telemetry.ts b/telemetry.ts index 2d9939b..08f98fc 100644 --- a/telemetry.ts +++ b/telemetry.ts @@ -6,7 +6,7 @@ * * // Early in your application entrypoint - pin to a specific version similar * // to what your main process use, like pup@1.0.0-alpha-28 - * import { PupTelemetry } from "https://deno.land/x/pup/telemetry.ts" + * import { PupTelemetry } from "jsr:@pup/pup/telemetry.ts" * const telemetry = PupTelemetry() * * // The rest of your application @@ -29,7 +29,7 @@ * @license MIT */ -import { EventEmitter, EventHandler } from "./lib/common/eventemitter.ts" +import { EventEmitter, type EventHandler } from "./lib/common/eventemitter.ts" import { FileIPC } from "./lib/common/ipc.ts" import { exists, isDir } from "@cross/fs" diff --git a/test/cli/columns.test.ts b/test/cli/columns.test.ts index d44f0e0..fb5e10a 100644 --- a/test/cli/columns.test.ts +++ b/test/cli/columns.test.ts @@ -1,5 +1,5 @@ import { assertEquals } from "@std/assert" -import { Column, Columns, SeparatorRow, TableRow } from "../../lib/cli/columns.ts" +import { type Column, Columns, type SeparatorRow, type TableRow } from "../../lib/cli/columns.ts" import { test } from "@cross/test" test("Formats and pads Rows and Columns correctly", () => { diff --git a/test/core/loadbalancer.test.ts b/test/core/loadbalancer.test.ts index f456ade..1558fd4 100644 --- a/test/core/loadbalancer.test.ts +++ b/test/core/loadbalancer.test.ts @@ -1,6 +1,6 @@ // load_balancer_test.ts import { assertEquals, assertThrows } from "@std/assert" -import { Backend, BalancingStrategy, hashCode, LoadBalancer } from "../../lib/core/loadbalancer.ts" +import { type Backend, BalancingStrategy, hashCode, LoadBalancer } from "../../lib/core/loadbalancer.ts" import { test } from "@cross/test" // Define logger callback function diff --git a/test/core/logger.test.ts b/test/core/logger.test.ts index 343125b..3d02ceb 100644 --- a/test/core/logger.test.ts +++ b/test/core/logger.test.ts @@ -1,6 +1,6 @@ import { assertEquals, assertGreater } from "@std/assert" -import { AttachedLogger, LogEventData, Logger } from "../../lib/core/logger.ts" -import { ProcessConfiguration } from "../../mod.ts" +import { type AttachedLogger, type LogEventData, Logger } from "../../lib/core/logger.ts" +import type { ProcessConfiguration } from "../../mod.ts" import { test } from "@cross/test" test("Logger - Creation with Global Configuration", () => { diff --git a/test/core/plugin.test.ts b/test/core/plugin.test.ts index 8d0f7c0..d71c91c 100644 --- a/test/core/plugin.test.ts +++ b/test/core/plugin.test.ts @@ -1,7 +1,7 @@ import { assertEquals, assertThrows } from "@std/assert" import { Pup } from "../../lib/core/pup.ts" import { Plugin, PluginApi } from "../../lib/core/plugin.ts" -import { PluginConfiguration } from "../../lib/core/configuration.ts" +import type { PluginConfiguration } from "../../lib/core/configuration.ts" import { test } from "@cross/test" const minimalPupConfiguration = { diff --git a/test/core/pup.test.ts b/test/core/pup.test.ts index ec50fc5..80c8c28 100644 --- a/test/core/pup.test.ts +++ b/test/core/pup.test.ts @@ -4,7 +4,7 @@ * @file test/core/pup.test.ts */ -import { Configuration } from "../../lib/core/configuration.ts" +import type { Configuration } from "../../lib/core/configuration.ts" import { ProcessState } from "../../lib/core/process.ts" import { Pup } from "../../lib/core/pup.ts" import { assertEquals, assertNotEquals } from "@std/assert" diff --git a/test/core/test-data/test_signal_listener.ts b/test/core/test-data/test_signal_listener.ts index 1592a2b..d668371 100644 --- a/test/core/test-data/test_signal_listener.ts +++ b/test/core/test-data/test_signal_listener.ts @@ -1,4 +1,4 @@ -import { PluginApi, PluginImplementation } from "../../../lib/core/plugin.ts" +import { type PluginApi, PluginImplementation } from "../../../lib/core/plugin.ts" export class PupPlugin extends PluginImplementation { public meta = { diff --git a/versions.json b/versions.json index 98eff88..eb63974 100644 --- a/versions.json +++ b/versions.json @@ -2,6 +2,20 @@ "canary_url": "https://raw.githubusercontent.com/Hexagon/pup/main/pup.ts", "stable": [], "prerelease": [ + { + "version": "1.0.0-rc.15", + "url": "https://jsr.io/@pup/pup/@1.0.0-rc.15/pup.ts", + "deno": null, + "deno_unstable": "1.42.0", + "default_permissions": [ + "--allow-env", + "--allow-read", + "--allow-write", + "--allow-sys=loadavg,systemMemoryInfo,osUptime,osRelease", + "--allow-net", + "--allow-run" + ] + }, { "version": "1.0.0-rc.14", "url": "https://deno.land/x/pup@1.0.0-rc.14/pup.ts", From e83f495d3349fc339f1f41becb7e921fd481bfa2 Mon Sep 17 00:00:00 2001 From: Hexagon Date: Sun, 14 Apr 2024 00:34:28 +0200 Subject: [PATCH 13/13] Narrow down the scope of 1.0.0-rc.15 --- docs/src/changelog.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/src/changelog.md b/docs/src/changelog.md index 7ccc99b..e3988c6 100644 --- a/docs/src/changelog.md +++ b/docs/src/changelog.md @@ -18,7 +18,6 @@ All notable changes to this project will be documented in this section. - [x] change(cli): Rename cli command run to foreground to not be confused with starting as a service - [x] change(cli): Rename cli command install to enable-service to separate from actual pup installation - [x] change(cli): Rename cli command uninstall to disable-service to separate from actual pup uninstallation -- [ ] change(logging): Make `pup logs` streaming by default. - [x] change(config): Support JSON5. - [x] change(core): Move .pup.jsonc-tmp, .pup.jsonc-data into .pup - [x] change(packaging): Move default installation docs/references from `deno.land/x` to JSR.io @@ -28,7 +27,6 @@ All notable changes to this project will be documented in this section. - [x] fix(core): Foreground command did not keep an autostarted process running, fixed by refing the watchdog timer. - [x] fix(cli): Controlled output of manual rests after installing/uninstalling as service. - [x] fix(docs): Docs incorrectly stated that `**/_._` is default for watcher config. `**/*.*` is correct. -- [ ] chore(docs): Add a PM2 migration guide. - [x] fix(schema): Expect Record for process.env in json schema. - [x] change(core): Make most code cross runtime, preparing for Node and Bun support.