UNPKG

rawx

Version:

process daemon with utilities

531 lines 23 kB
"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