UNPKG

seneca

Version:

A Microservices Framework for Node.js

647 lines 20.7 kB
/* Copyright © 2010-2022 Richard Rodger and other contributors, MIT License. */ 'use strict'; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TRACE_ACTION = exports.TRACE_SYNC = exports.TRACE_END = exports.TRACE_START = exports.TRACE_VERSION = exports.TRACE_TAG = exports.TRACE_INSTANCE = exports.TRACE_ID = exports.TRACE_PATTERN = exports.error = exports.tagnid = exports.print = void 0; exports.promiser = promiser; exports.stringify = stringify; exports.wrap_error = wrap_error; exports.make_plugin_key = make_plugin_key; exports.boolify = boolify; exports.parse_jsonic = parse_jsonic; exports.pattern = pattern; exports.pincanon = pincanon; exports.noop = noop; exports.clean = clean; exports.deep = deep; exports.each = each; exports.makedie = makedie; exports.make_standard_act_log_entry = make_standard_act_log_entry; exports.make_standard_err_log_entry = make_standard_err_log_entry; exports.resolve_option = resolve_option; exports.autoincr = autoincr; exports.make_callpoint = make_callpoint; exports.make_trace_desc = make_trace_desc; exports.history = history; exports.inspect = inspect; exports.msgstr = msgstr; exports.jsonic_stringify = jsonic_stringify; const util_1 = __importDefault(require("util")); const fast_safe_stringify_1 = __importDefault(require("fast-safe-stringify")); const jsonic_1 = __importDefault(require("jsonic")); const nid_1 = __importDefault(require("nid")); const Eraro = require('eraro'); const DefaultsDeep = require('lodash.defaultsdeep'); const { Print } = require('./print'); const errors_1 = __importDefault(require("./errors")); // const Argu = MakeArgu('seneca') const error = (exports.error = exports.eraro = Eraro({ package: 'seneca', msgmap: errors_1.default, override: true, })); exports.error = error; function promiser(context, callback) { if ('function' === typeof context && null == callback) { callback = context; } else { callback = callback.bind(context); } return new Promise((resolve, reject) => { callback((err, out) => { return err ? reject(err) : resolve(out); }); }); } function stringify() { return fast_safe_stringify_1.default(...arguments); } function wrap_error(err) { if (err.seneca) { throw err; } else { throw error.call(null, ...arguments); } } function make_plugin_key(plugin, origtag) { if (null == plugin) { throw error('missing_plugin_name'); } let name = null == plugin.name ? plugin : plugin.name; let tag = null == plugin.tag ? (null == origtag ? '' : origtag) : plugin.tag; if ('number' === typeof name) { name = '' + name; } if ('number' === typeof tag) { tag = '' + tag; } if ('' == name || 'string' !== typeof name) { throw error('bad_plugin_name', { name: name }); } let m = name.match(/^([a-zA-Z@][a-zA-Z0-9.~_\-/]*)\$([a-zA-Z0-9.~_-]+)$/); if (m) { name = m[1]; tag = m[2]; } // Allow file paths, but ... if (!name.match(/^(\.|\/|\\|\w:)/)) { // ... anything else should be well-formed if (!name.match(/^[a-zA-Z@][a-zA-Z0-9.~_\-/]*$/) || 1024 < name.length) { throw error('bad_plugin_name', { name: name }); } } if ('' != tag && (!tag.match(/^[a-zA-Z0-9.~_-]+$/) || 1024 < tag.length)) { throw error('bad_plugin_tag', { tag: tag }); } let key = name + (tag ? '$' + tag : ''); return key; } function boolify(v) { try { return !!JSON.parse(v); } catch (e) { return false; } } const tagnid = (0, nid_1.default)({ length: 3, alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' }); exports.tagnid = tagnid; function parse_jsonic(str, code) { code = code || 'bad_jsonic'; try { return null == str ? null : (0, jsonic_1.default)(str); } catch (e) { throw error(code, { argstr: str, syntax: e.message, line: e.lineNumber, col: e.columnNumber, }); } } // Convert pattern object into a normalized jsonic String. function pattern(patobj) { if ('string' === typeof patobj) { return patobj; } patobj = patobj || {}; let sb = []; Object.keys(patobj).forEach((k) => { let v = patobj[k]; if (!~k.indexOf('$') && 'function' != typeof v && 'object' != typeof v) { sb.push(k + ':' + v); } }); sb.sort(); return sb.join(','); } function pincanon(inpin) { if ('string' == typeof inpin) { return pattern((0, jsonic_1.default)(inpin)); } else if (Array.isArray(inpin)) { let pin = inpin.map(pincanon); pin.sort(); return pin.join(';'); } else { return pattern(inpin); } } function noop() { } // remove any props containing $ function clean(obj, opts) { if (null == obj) return obj; let out = Array.isArray(obj) ? [] : {}; let pn = Object.getOwnPropertyNames(obj); for (let i = 0; i < pn.length; i++) { let p = pn[i]; if ('$' != p[p.length - 1]) { out[p] = obj[p]; } } return out; } // rightmost wins function deep(...argsarr) { // Lodash uses the reverse order to apply defaults than the deep API. argsarr = argsarr.reverse(); // Add an empty object to the front of the args. Defaults will be written // to this empty object. argsarr.unshift({}); return DefaultsDeep.apply(null, argsarr); } // Print action result const print = Print.print; exports.print = print; // Iterate over arrays or objects function each(collect, func) { if (null == collect || null == func) { return null; } if (Array.isArray(collect)) { return collect.forEach(func); } else { Object.keys(collect).forEach((k) => func(collect[k], k)); } } function makedie(instance, ctxt) { ctxt = Object.assign(ctxt, instance.die ? instance.die.context : {}); let diecount = 0; let die = function (err) { let so = instance.options(); let test = so.test; // undead is only for testing, do not use in production let undead = (so.debug && so.debug.undead) || (err && err.undead); let full = (so.debug && so.debug.print && 'full' === so.debug.print.fatal) || false; let print_env = (so.debug && so.debug.print.env) || false; if (0 < diecount) { if (!undead) { throw error(err, '[DEATH LOOP] die count: ' + diecount); } return; } else { diecount++; } try { if (!err) { err = new Error('unknown'); // } else if (!Util.isError(err)) { } else if (!so.error.identify(err)) { err = new Error('string' === typeof err ? err : inspect(err)); } err.fatal$ = true; let logdesc = { kind: ctxt.txt || 'fatal', level: ctxt.level || 'fatal', plugin: ctxt.plugin, tag: ctxt.tag, id: ctxt.id, code: err.code || 'fatal', notice: err.message, err: err, callpoint: ctxt.callpoint && ctxt.callpoint(), }; instance.log.fatal(logdesc); let stack = err.stack || ''; stack = stack .substring(stack.indexOf('\n') + 5) .replace(/\n\s+/g, '\n '); let procdesc = 'pid=' + process.pid + ', arch=' + process.arch + ', platform=' + process.platform + (!full ? '' : ', path=' + process.execPath) + ', argv=' + inspect(process.argv).replace(/\n/g, '') + (!full ? '' : !print_env ? '' : ', env=' + inspect(process.env).replace(/\n/g, '')); let when = new Date(); let clean_details = null; let stderrmsg = '\n\n' + '=== SENECA FATAL ERROR ===' + '\nMESSAGE ::: ' + err.message + '\nCODE ::: ' + err.code + '\nINSTANCE ::: ' + instance.toString() + '\nDETAILS ::: ' + inspect(full ? err.details : ((clean_details = clean(err.details) || {}), delete clean_details.instance, clean_details), { depth: so.debug.print.depth }).replace(/\n/g, '\n ') + '\nSTACK ::: ' + stack + '\nWHEN ::: ' + when.toISOString() + ', ' + when.getTime() + '\nLOG ::: ' + jsonic_stringify(logdesc) + '\nNODE ::: ' + process.version + ', ' + process.title + (!full ? '' : ', ' + inspect(process.versions).replace(/\s+/g, ' ') + ', ' + inspect(process.features).replace(/\s+/g, ' ') + ', ' + inspect(process.moduleLoadList).replace(/\s+/g, ' ')) + '\nPROCESS ::: ' + procdesc + '\nFOLDER ::: ' + process.env.PWD; if (so.errhandler) { so.errhandler.call(instance, err); } if (instance.flags.closed) { return; } if (!undead) { instance.act('role:seneca,info:fatal,closing$:true', { err: err }); instance.close( // terminate process, err (if defined) is from seneca.close function (close_err) { if (!undead) { process.nextTick(function () { if (close_err) { instance.log.fatal({ kind: 'close', err: inspect(close_err), }); } if (test) { if (close_err) { Print.internal_err(close_err); } Print.internal_err(stderrmsg); Print.internal_err('\nSENECA TERMINATED at ' + new Date().toISOString() + '. See above for error report.\n'); } so.system.exit(1); }); } }); } // make sure we close down within options.death_delay seconds if (!undead) { // console.log('KILL', err) // console.trace() let killtimer = setTimeout(function () { instance.log.fatal({ kind: 'close', timeout: true }); if (so.test) { Print.internal_err(stderrmsg); Print.internal_err('\n\nSENECA TERMINATED (on timeout) at ' + new Date().toISOString() + '.\n\n'); } so.system.exit(2); }, so.death_delay); if (killtimer.unref) { killtimer.unref(); } } } catch (panic) { this.log.fatal({ kind: 'panic', panic: inspect(panic), orig: arguments[0], }); if (so.test) { let msg = '\n\n' + 'Seneca Panic\n' + '============\n\n' + panic.stack + '\n\nOriginal Error:\n' + (arguments[0] && arguments[0].stack ? arguments[0].stack : arguments[0]); Print.internal_err(msg); } } }; die.context = ctxt; return die; } function make_standard_act_log_entry(actdef, msg, meta, origmsg, ctxt) { let transport = origmsg.transport$ || {}; let callmeta = meta || msg.meta$ || {}; let prior = callmeta.prior || {}; actdef = actdef || {}; return Object.assign({ actid: callmeta.id, msg: msg, meta: meta, entry: prior.entry, prior: prior.chain, gate: origmsg.gate$, caller: origmsg.caller$, actdef: actdef, // these are transitional as need to be updated // to standard transport metadata client: actdef.client, listen: !!transport.origin, transport: transport, }, ctxt); } function make_standard_err_log_entry(err, ctxt) { if (!err) return ctxt; if (err.details && ctxt && ctxt.caller) { err.details.caller = ctxt.caller; } let entry = Object.assign({ notice: err.message, code: err.code, err: err, }, ctxt); return entry; } function resolve_option(value, options) { return 'function' === typeof value ? value(options) : value; } function autoincr() { let counter = 0; return function () { return counter++; }; } function inspect(val, opts) { return util_1.default.inspect(val, opts); } // Callpoint resolver. Indicates location in calling code. function make_callpoint(active) { return function callpoint(override) { if (active || override) { return error.callpoint(new Error(), [ '/ordu.js', '/seneca/seneca.js', '/seneca/lib/', '/lodash.js', ]); } else { return void 0; } }; } function make_trace_desc(meta) { return [ meta.pattern, meta.id, meta.instance, meta.tag, meta.version, meta.start, meta.end, meta.sync, meta.action, ]; } // Stringify message for logs, debugging and errors. function msgstr(msg, len = 111) { let str = inspect(clean(msg)) .replace(/\n/g, ''); str = str.substring(0, len) + (len < str.length ? '...' : ''); return str; } function jsonic_strify(val, opts, depth) { depth++; if (null == val) return 'null'; var type = Object.prototype.toString.call(val).charAt(8); if ('F' === type && !opts.showfunc) return null; // WARNING: output may not be jsonically parsable! if (opts.custom) { if (Object.prototype.hasOwnProperty.call(val, 'toString')) { return val.toString(); } else if (Object.prototype.hasOwnProperty.call(val, 'inspect')) { return val.inspect(); } } var out, i = 0, j, k; if ('N' === type) { return isNaN(val) ? 'null' : val.toString(); } else if ('O' === type) { out = []; if (depth <= opts.depth) { j = 0; for (let i in val) { if (j >= opts.maxitems) break; var pass = true; for (k = 0; k < opts.exclude.length && pass; k++) { pass = !~i.indexOf(opts.exclude[k]); } pass = pass && !opts.omit[i]; let str = jsonic_strify(val[i], opts, depth); if (null != str && pass) { var n = i.match(/^[a-zA-Z0-9_$]+$/) ? i : JSON.stringify(i); out.push(n + ':' + str); j++; } } } return '{' + out.join(',') + '}'; } else if ('A' === type) { out = []; if (depth <= opts.depth) { for (; i < val.length && i < opts.maxitems; i++) { let str = jsonic_strify(val[i], opts, depth); if (null != str) { out.push(str); } } } return '[' + out.join(',') + ']'; } else { var valstr = val.toString(); if (~" \"'\r\n\t,}]".indexOf(valstr[0]) || !~valstr.match(/,}]/) || ~" \r\n\t".indexOf(valstr[valstr.length - 1])) { valstr = "'" + valstr.replace(/'/g, "\\'") + "'"; } return valstr; } } // Legacy Jsonic stringify function jsonic_stringify(val, callopts) { try { callopts = callopts || {}; var opts = {}; opts.showfunc = callopts.showfunc || callopts.f || false; opts.custom = callopts.custom || callopts.c || false; opts.depth = callopts.depth || callopts.d || 3; opts.maxitems = callopts.maxitems || callopts.mi || 11; opts.maxchars = callopts.maxchars || callopts.mc || 111; opts.exclude = callopts.exclude || callopts.x || ['$']; var omit = callopts.omit || callopts.o || []; opts.omit = {}; for (var i = 0; i < omit.length; i++) { opts.omit[omit[i]] = true; } var str = jsonic_strify(val, opts, 0); str = null == str ? '' : str.substring(0, opts.maxchars); return str; } catch (e) { return 'ERROR: jsonic_stringify: ' + e + ' input was: ' + JSON.stringify(val); } } const TRACE_PATTERN = 0; exports.TRACE_PATTERN = TRACE_PATTERN; const TRACE_ID = 1; exports.TRACE_ID = TRACE_ID; const TRACE_INSTANCE = 2; exports.TRACE_INSTANCE = TRACE_INSTANCE; const TRACE_TAG = 3; exports.TRACE_TAG = TRACE_TAG; const TRACE_VERSION = 4; exports.TRACE_VERSION = TRACE_VERSION; const TRACE_START = 5; exports.TRACE_START = TRACE_START; const TRACE_END = 6; exports.TRACE_END = TRACE_END; const TRACE_SYNC = 7; exports.TRACE_SYNC = TRACE_SYNC; const TRACE_ACTION = 8; exports.TRACE_ACTION = TRACE_ACTION; function history(opts) { return new ActHistory(opts); } class ActHistory { constructor(opts) { this._total = 0; this._list = []; this._map = {}; let self = this; opts = opts || {}; if (opts.prune) { this._prune_interval = setInterval(function () { self.prune(Date.now()); }, opts.interval || 100); if (this._prune_interval.unref) { this._prune_interval.unref(); } } } stats() { return { total: this._total, }; } add(obj) { this._map[obj.id] = obj; let i = this._list.length - 1; if (i < 0 || this._list[i].timelimit <= obj.timelimit) { this._list.push(obj); } else { i = this.place(obj.timelimit); this._list.splice(i, 0, obj); } } place(timelimit) { let i = this._list.length; let s = 0; let e = i; if (0 === this._list.length) { return 0; } do { i = Math.floor((s + e) / 2); if (timelimit > this._list[i].timelimit) { s = i + 1; i = s; } else if (timelimit < this._list[i].timelimit) { e = i; } else { i++; break; } } while (s < e); return i; } prune(timelimit) { let i = this.place(timelimit); if (0 <= i && i <= this._list.length) { for (let j = 0; j < i; j++) { delete this._map[this._list[j].id]; } this._list = this._list.slice(i); } } get(id) { return this._map[id] || null; } list() { return this._list; } close() { if (this._prune_interval) { clearInterval(this._prune_interval); } } toString() { return inspect({ total: this._total, map: this._map, list: this._list, }); } [util_1.default.inspect.custom]() { return this.toString(); } } //# sourceMappingURL=common.js.map