rawx
Version:
process daemon with utilities
531 lines • 23 kB
JavaScript
"use strict";
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
/***
* license.kind
* Original: https://github.com/lilzeta
* Flux this tag if/whenever you feel like
*/
// module.exports = Server_Constructor;
// externals
const { v4: __id } = require("uuid");
const { format } = require("util");
// internal modules/classes
const Watch = require("./watch");
const ops_mod = require("../ops/index");
const Proc_Util = require("./proc_util");
const Validator = require("../util/validation/validator");
// set in constructor
let o;
// WIP validator class elsewhere
const server_mins = {
kill_delay: 50,
exit_delay: 50,
// pulse: 400,
};
const Truncate_Label = 100;
// Server_Facade behaves as would exposed inner _Server
class Server_Constructor_Facade {
constructor(args) {
return server_constructor_creator(args);
}
}
// Now we expose _Server through Server_Facade as if we created it w/vanilla
const Server_Constructor = Server_Constructor_Facade;
const server_constructor_creator = (
// const Server_Constructor_Creator: Class_Proxy_F<Server_Args, Server_Constructor_I> = (
args) => {
// Server_Creator => return new _Server(args);
class Server_Constructor_Concrete {
constructor(args) {
var _a;
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: ""
});
Object.defineProperty(this, "label", {
enumerable: true,
configurable: true,
writable: true,
value: ""
}); // Not an arg, name.translated
// 0-10 recommended, sweep the etc critical 11/12...
Object.defineProperty(this, "debug", {
enumerable: true,
configurable: true,
writable: true,
value: 2
});
// aka wait for port to clear
Object.defineProperty(this, "kill_delay", {
enumerable: true,
configurable: true,
writable: true,
value: 3000
}); // in ms
// yet to determine if we need to allow last proc any log time
Object.defineProperty(this, "exit_delay", {
enumerable: true,
configurable: true,
writable: true,
value: 3000
}); // in ms
Object.defineProperty(this, "watch", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_watch", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
// These are aliased into Watch for now
Object.defineProperty(this, "trigger_index", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "trigger_indices", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "colors", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
// https://stackoverflow.com/questions/40510611/typescript-interface-require-one-of-two-properties-to-exist
Object.defineProperty(this, "procs", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
}); // used to reset stack if trigger_index
// likely uncommon first is not 0
Object.defineProperty(this, "first_proc", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
Object.defineProperty(this, "_step_procs", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
}); // used as current stack
Object.defineProperty(this, "_range_cache", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_last_range_at", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
}); // is the last range cache valid?
Object.defineProperty(this, "_proc_util", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
// uuid of the most recent chain
Object.defineProperty(this, "_tubed", {
enumerable: true,
configurable: true,
writable: true,
value: undefined
});
Object.defineProperty(this, "_running", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
Object.defineProperty(this, "_live_functions", {
enumerable: true,
configurable: true,
writable: true,
value: {}
});
// out?: str;
Object.defineProperty(this, "_validator", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
// Proc to _Proc
Object.defineProperty(this, "check_proc_valid_then_coerce", {
enumerable: true,
configurable: true,
writable: true,
value: (proc) => {
// disallow circularly concurrent chain, edit source if you a bold one _lol`
if (proc._proc_id)
return undefined;
proc = proc;
let label;
if (!proc.type) {
const err = `Server [142] - type && command missing, Fatal proc: ${o.pretty(proc)}`;
throw new Error(err);
}
if (this._proc_util.is_fn_proc(proc)) {
let fn_proc = proc;
label = o.pretty(fn_proc.fn);
label = o.truncate(label, Truncate_Label);
// shouldn't get a warn if type-safe? - fatal
if (!fn_proc.fn) {
const err = `Server [142] - fn missing from fn proc, Fatal. proc: ${o.pretty(proc)}`;
throw new Error(err);
}
}
else {
let proc_ = proc;
if (!proc_.command) {
const err = `Server [142] - command missing from proc, Fatal. proc: ${o.pretty(proc)}`;
throw new Error(err);
}
label = proc_.command;
}
return this.Proc_Arg_As_A_Proc(proc, label);
}
});
// I like turtles
Object.defineProperty(this, "no_circular_tree_map", {
enumerable: true,
configurable: true,
writable: true,
value: (proc) => {
var _a;
// because A_Proc_Arg.concurrent could be in tree as _A_Proc already
if (proc._proc_id)
return undefined;
// since no proc_id, proc is a A_Proc_Arg (not circular)
proc = proc;
let label;
if (this._proc_util.is_fn_proc(proc)) {
let fn_proc = proc;
// util.format for label
label = format(fn_proc.fn);
}
else {
// TODO (more like this)
if (proc.type === "exec") {
if (proc.args)
throw new Error(`proc.args are not allowed with {type: exec}`);
label = o.truncate(`[${proc.type}] - ${proc.command}] `, Truncate_Label);
}
else if (proc.type === "fork") {
let _proc = proc;
// TODO
label = o.truncate(`[${_proc.type}](${_proc.module}] `, Truncate_Label);
}
else {
let _proc = proc;
let args_s = Array.isArray(_proc.args)
? (_a = _proc.args) === null || _a === void 0 ? void 0 : _a.join(", ")
: _proc.args;
label = o.truncate(`[${_proc.type}](${_proc.command} args:[${args_s}]`, Truncate_Label);
}
}
return this.Proc_Arg_As_A_Proc(proc, label);
}
});
if (!args)
throw new Error("No server configuration was provided, nothing to do.");
// if (args.dry_run) {
// // Block is for ./src/scripts/build_json_validator.js (strip on pack?)
// o = new ops_mod.Ops();
// this._proc_util = new Proc_Util({ inherit_ops: o });
// this.setup_procs({ procs: args.procs, proc: args.proc });
// this._validator = new Validator("server", server_schema);
// this._validator.validate(args);
// return;
// }
// this._validator = new Validator("server", server_schema);
// Throws iff any invalid
// this._validator.validate(args);
// this._validator.set_validated(this);
const { name, watch, colors } = args, opts = __rest(args, ["name", "watch", "colors"]);
const { trigger_index, trigger_indices } = opts;
if (args.debug !== undefined) {
this.debug = args.debug;
}
if ((_a = args.name) === null || _a === void 0 ? void 0 : _a.length) {
this.name = args.name;
this.label = `${args.name}|Server`;
}
// First global call sets the default cache/base
// this sets server args as a default basis
o = new ops_mod.Ops({
colors: args.colors,
debug: this.debug,
label: this.label,
});
this.check_valid_and_assign(args);
let { procs, proc } = opts;
this.watch = watch;
if (o.defi(colors))
this.colors = colors;
this._proc_util = new Proc_Util({ inherit_ops: o });
this.setup_procs({ procs, proc });
if (watch) {
this.trigger_index = trigger_index;
this.trigger_indices = trigger_indices;
this.validate_triggers();
this.setup_watch();
}
o.log(6, `Server_Construct constructor has completed, without known errors for Server`);
}
validate_triggers() {
if (o.defi(this.trigger_index) &&
this.trigger_index > this.procs.length)
throw new Error(`trigger_index must not be larger than procs size.`);
if (o.defi(this.trigger_indices)) {
if (this.trigger_index)
throw new Error(`trigger_index cannot be used with trigger_indices.`);
const trigger_indices = this.trigger_indices;
const q = "Pass undefined to skip indexes, ";
// WIP !! complex[0].paths // this.trigger_indices.length
if (this.watch.complex) {
// TODO better validator
if (this.watch.complex.complex[0].paths.length !== trigger_indices.length)
throw new Error(`${q}trigger_indices.length must match watch.complex...paths.length.`);
}
else {
if (this.watch.paths.length !== trigger_indices.length) {
throw new Error(`${q}trigger_indices.length must match watch.paths.length.`);
}
} // WIP validation
// throw new Error(`trigger_index must not be larger than procs size.`);
}
}
setup_watch() {
var _a;
const watch_args = this.watch;
const parent_colors = this.colors;
o.log(7, "L:223", `setup_watch - watch.complex: `);
o.log(7, "L:224", watch_args.complex);
if (watch_args.complex) {
this.setup_multi_watch();
return;
}
// convert singular path to an array
if (typeof (watch_args === null || watch_args === void 0 ? void 0 : watch_args.paths) === "string") {
watch_args.paths = [watch_args.paths];
}
if ((_a = watch_args === null || watch_args === void 0 ? void 0 : watch_args.paths) === null || _a === void 0 ? void 0 : _a.length) {
this._watch = new Watch(Object.assign(Object.assign(Object.assign({ name: this.name, debug: this.debug }, o.puff("colors", parent_colors)), { trigger_index: this.trigger_index, trigger_indices: this.trigger_indices }), watch_args));
}
}
setup_multi_watch() {
this._watch = new Watch(Object.assign({ name: this.name, colors: Object.assign({}, this.colors) }, this.watch));
}
// map proc/procs to arr of _Proc ... &some sensible checks
setup_procs({ procs: input_procs, proc: input_proc }) {
const NO_PROC_ERROR = `Server requires one of procs or proc, to contain a proc.`;
if (!input_procs && !input_proc) {
throw new Error(NO_PROC_ERROR);
}
if (input_procs && input_proc) {
throw new Error("Server accepts only one of procs | proc");
}
const input = (input_procs || input_proc);
let procs;
if (Array.isArray(input)) {
procs = input;
}
else {
procs = [input];
}
if (!procs.length)
throw new Error();
this.procs = [];
// undefined happens if self/circular ref found (is not allowed _yet_)
let jiggler;
for (let proc of procs) {
(jiggler = this.no_circular_tree_map(proc)) && this.procs.push(jiggler);
}
if (this.first_proc >= this.procs.length)
throw new Error(`first_proc: ${this.first_proc} is not in range of procs: ${this.procs.length}`);
this.set_range(this.first_proc);
}
Proc_Arg_As_A_Proc(proc_arg, label) {
let _conc;
if (proc_arg.concurrent) {
_conc = this.no_circular_tree_map(proc_arg.concurrent);
// just for cleaner logging of _Proc
delete proc_arg.concurrent;
}
o.accent(11, "L:368", `_conc in_tree? : ${_conc}`);
let as_internal_proc = Object.assign(proc_arg, Object.assign(Object.assign({ proc_id: __id() }, (_conc && {
_conc,
})), { _sidecar: Object.assign(Object.assign({}, ((label.length && {
label,
}) ||
"")), o.puff("silence", proc_arg.silence)) }));
// This just preps each proc up front so we only need to once
if (proc_arg.type === "exec") {
const process_proc = as_internal_proc;
process_proc.construct = {
type: proc_arg.type,
command: proc_arg.command,
};
return process_proc;
}
else if (!this._proc_util.is_fn_proc(as_internal_proc)) {
const process_proc = as_internal_proc;
process_proc.construct = this.set_run_proc_construct(proc_arg);
return process_proc;
}
// TODO is this complete? no right?
return as_internal_proc;
}
// at should be undefined on first call, intentionally
// Shallow clone so flash proc changes {...}
set_range(n) {
if (n === this._last_range_at) {
this._step_procs = [...this._range_cache];
return;
}
this._last_range_at = n;
this._range_cache = [];
for (let i = n; i < this.procs.length; i++)
this._range_cache.push(this.procs[i]);
this._step_procs = [...this._range_cache];
}
// TODO this is hacky, needs not unsafe setters
set_self(arg_name, value) {
this[arg_name] = value;
}
// TODO this is hacky, needs not unsafe setters
// get_self = (arg_name: keyof Server_Construct) => (this as any)[arg_name].value;
check_valid_and_assign(args) {
const str_keyed = ["name"];
str_keyed.forEach((arg_name) => {
if (o.defi(args[arg_name])) {
if (typeof args[arg_name] === "string") {
this.set_self(arg_name, args[arg_name]);
}
else {
throw new Error(`Use a number type arg to set Server.${String(arg_name)}.`);
}
}
else {
if (args[arg_name] === null) {
this.set_self(arg_name, undefined);
}
}
// else ignore undefined/false
});
const numeric = [
"debug",
"trigger_index",
"kill_delay",
"first_proc",
];
numeric.forEach((arg_name) => {
let value;
if (args[arg_name] !== undefined) {
if (args[arg_name] === null) {
// simple shortcut to some minimum set below
value = 0;
}
else if (typeof args[arg_name] === "number") {
value = args[arg_name];
}
else {
throw new Error(`Use a number type arg to set Server.${String(arg_name)}.`);
}
}
else
return; // default remains
if (o.defi(value)) {
// Safe usage is still the responsibility of the user
if (o.defi(server_mins[String(arg_name)])) {
value = Math.max(value, server_mins[String(arg_name)]);
}
this.set_self(arg_name, value);
}
});
// TODO that generics trick for methods
if (o.defi(args.trigger_indices)) {
if (args.trigger_indices.length !== args.watch.paths.length) {
let err = "args.trigger_indices.length should equal watch.paths.length";
err += "passing undefined for a path without a trigger is effective, ";
err += "or pass no trigger_indices";
throw new Error(err);
}
this.trigger_indices = args.trigger_indices;
}
}
set_run_proc_construct(_a) {
var { type, command } = _a, opts = __rest(_a, ["type", "command"]);
let { cwd, shell } = opts;
let { args } = opts;
if (o.defi(args)) {
if (!Array.isArray(args)) {
if (typeof args !== "string")
throw new Error(`proc.args is not an array, or string`);
args = [args];
}
}
else {
args = [];
}
// TODO test
// https://stackoverflow.com/questions/37459717/error-spawn-enoent-on-windows)
shell = (shell !== null && shell !== void 0 ? shell : process.platform == "win32") ? true : undefined;
// TODO testing
return Object.assign({ type,
command,
args }, ((cwd || shell) && {
options: Object.assign(Object.assign({}, (cwd && { cwd })), (shell && { shell })),
}));
}
}
return new Server_Constructor_Concrete(args);
};
module.exports = Server_Constructor;
// WIP/inactive
// _tube_lock?: bool;
// if (this.defi(args.override_trigger)) {
// this.restart = async (file_path: str) => {
// await this.kill_all();
// await wait(this.kill_delay);
// if (o.defi(this.trigger_index)) {
// this.set_range(this.trigger_index);
// this.step_procs = [...this.range_cache];
// }
// args.override_trigger(file_path);
// };
// }
// pulse: {
// // WIP chaperone (~watch dist oops)
// last_stamps: Array<number>;
// };
// hash: any = {};
//# sourceMappingURL=server_construct.js.map