lib/add.js
"use strict";
/* Copyright © 2019-2023 Richard Rodger and other contributors, MIT License. */
Object.defineProperty(exports, "__esModule", { value: true });
exports.task = exports.api_add = void 0;
const gubu_1 = require("gubu");
const common_1 = require("./common");
const Argu = (0, gubu_1.MakeArgu)('seneca');
const AddArgu = Argu('add', {
props: (0, gubu_1.One)((0, gubu_1.Empty)(String), Object),
moreprops: (0, gubu_1.Skip)(Object),
action: (0, gubu_1.Skip)(Function),
actdef: (0, gubu_1.Skip)(Object),
});
function api_add() {
let args = AddArgu(arguments);
args.pattern = Object.assign({}, args.moreprops ? args.moreprops : null, 'string' === typeof args.props ?
(0, common_1.parse_jsonic)(args.props, 'add_string_pattern_syntax') :
args.props);
let ctx = {
opts: this.options(),
args,
private: this.private$,
instance: this,
};
let data = {
actdef: null,
pattern: null,
action: null,
strict_add: null,
addroute: null,
};
// Senecca.order.add tasks are defined in main seneca file.
let res = this.order.add.execSync(ctx, data);
if (res.err) {
throw res.err;
}
return this;
}
exports.api_add = api_add;
const task = {
translate(spec) {
const args = spec.ctx.args;
let raw_pattern = args.pattern;
let pattern = null;
if (false !== raw_pattern.translate$) {
// Must be exact match to ensure consistent translation
let translation = spec.ctx.private.translationrouter.find(raw_pattern);
if (translation) {
pattern = translation(raw_pattern);
}
}
return {
// TODO: simple op:set would be faster
op: 'merge',
out: {
pattern,
},
};
},
prepare(spec) {
const args = spec.ctx.args;
let raw_pattern = spec.data.pattern || args.pattern;
let pattern = (0, common_1.clean)(raw_pattern);
let action = args.action ||
function default_action(_msg, done, meta) {
if (meta) {
done.call(this, null, meta.dflt || null, meta);
}
else {
done.call(this, null, null);
}
};
let actdef = (0, common_1.deep)(args.actdef) || {};
actdef.raw = (0, common_1.deep)({}, raw_pattern);
return {
// TODO: simple op:set would be faster
op: 'merge',
out: {
actdef,
action,
pattern,
},
};
},
plugin(spec) {
const actdef = spec.data.actdef;
// TODO: change root$ to root as plugin_name should not contain $
actdef.plugin_name = actdef.plugin_name || 'root$';
actdef.plugin_fullname =
actdef.plugin_fullname ||
actdef.plugin_name +
((actdef.plugin_tag === '-' ? void 0 : actdef.plugin_tag)
? '$' + actdef.plugin_tag
: '');
actdef.plugin = {
name: actdef.plugin_name,
tag: actdef.plugin_tag,
fullname: actdef.plugin_fullname,
};
return {
// TODO: simple op:set would be faster
op: 'merge',
out: {
actdef,
},
};
},
callpoint(spec) {
const private$ = spec.ctx.private;
const actdef = spec.data.actdef;
let add_callpoint = private$.callpoint();
if (add_callpoint) {
actdef.callpoint = add_callpoint;
}
return {
// TODO: simple op:set would be faster
op: 'merge',
out: {
actdef,
},
};
},
flags(spec) {
const actdef = spec.data.actdef;
const pat = spec.data.pattern;
const opts = spec.ctx.opts;
const Jsonic = spec.ctx.instance.util.Jsonic;
actdef.sub = !!actdef.raw.sub$;
actdef.client = !!actdef.raw.client$;
// Deprecate a pattern by providing a string message using deprecate$ key.
actdef.deprecate = actdef.raw.deprecate$;
actdef.fixed = Jsonic(actdef.raw.fixed$ || {});
actdef.custom = Jsonic(actdef.raw.custom$ || {});
var strict_add = actdef.raw.strict$ && actdef.raw.strict$.add !== null
? !!actdef.raw.strict$.add
: !!opts.strict.add;
if (opts.legacy.actdef) {
actdef.args = (0, common_1.deep)(pat);
}
// Canonical string form of the action pattern.
actdef.pattern = (0, common_1.pattern)(pat);
// Canonical object form of the action pattern.
actdef.msgcanon = Jsonic(actdef.pattern);
// Canonical string form of the action pattern.
actdef.pattern = (0, common_1.pattern)(pat);
// Canonical object form of the action pattern.
actdef.msgcanon = Jsonic(actdef.pattern);
return {
// TODO: simple op:set would be faster
op: 'merge',
out: {
actdef,
strict_add,
},
};
},
action(spec) {
const actdef = spec.data.actdef;
const action = spec.data.action;
const private$ = spec.ctx.private;
const plugin = actdef.plugin || {};
const action_name = ((null == action.name || '' === action.name) ?
'action' : action.name);
actdef.id =
((null == plugin.fullname || '' === plugin.fullname) ?
'' : plugin.fullname + '/') +
action_name + '/' + private$.next_action_id();
actdef.name = action_name;
actdef.func = action;
if ('function' === typeof action.handle) {
actdef.handle = action.handle;
}
return {
// TODO: simple op:set would be faster
op: 'merge',
out: {
actdef,
},
};
},
prior(spec) {
const actdef = spec.data.actdef;
const pattern = spec.data.pattern;
const strict_add = spec.data.strict_add;
const instance = spec.ctx.instance;
let addroute = true;
var priordef = instance.find(pattern);
if (priordef && strict_add && priordef.pattern !== actdef.pattern) {
// only exact action patterns are overridden
// use .wrap for pin-based patterns
priordef = null;
}
if (priordef) {
// Clients needs special handling so that the catchall
// pattern does not submit all patterns into the handle
if ('function' === typeof priordef.handle &&
((priordef.client && actdef.client) ||
(!priordef.client && !actdef.client))) {
priordef.handle(actdef);
addroute = false;
}
else {
actdef.priordef = priordef;
}
actdef.priorpath = priordef.id + ';' + priordef.priorpath;
}
else {
actdef.priorpath = '';
}
return {
// TODO: simple op:set would be faster
op: 'merge',
out: {
actdef,
addroute,
},
};
},
// TODO: accept action.valid to define rules
rules(spec) {
const opts = spec.ctx.opts;
const actdef = spec.data.actdef;
const pattern = spec.data.pattern;
// TODO: provide option for seneca-joi etc to disable Gubu
let pattern_rules = {};
let external = false; // external rule engine
let prN = 0;
(0, common_1.each)(actdef.raw, function (v, k) {
if (null != v &&
('object' === typeof v || 'function' === typeof v) &&
!~k.indexOf('$')) {
pattern_rules[k] = v;
prN++;
delete pattern[k];
// Recognize joi
if (v.$_root) {
external = true;
}
}
});
actdef.rules = pattern_rules;
if (!opts.legacy.rules && !external && 0 < prN) {
// TODO: how to make Closed if specified by user ?
actdef.gubu = (0, gubu_1.Gubu)((0, gubu_1.Open)(pattern_rules));
}
return {
// TODO: simple op:set would be faster
op: 'merge',
out: {
actdef,
},
};
},
register(spec) {
const actdef = spec.data.actdef;
const pattern = spec.data.pattern;
const addroute = spec.data.addroute;
const private$ = spec.ctx.private;
const instance = spec.ctx.instance;
if (addroute) {
instance.log.debug({
kind: 'add',
case: actdef.sub ? 'SUB' : 'ADD',
action: actdef.id,
pattern: actdef.pattern,
callpoint: private$.callpoint(true),
});
private$.actrouter.add(pattern, actdef);
}
private$.stats.actmap[actdef.pattern] =
private$.stats.actmap[actdef.pattern] || intern.make_action_stats(actdef);
private$.actdef[actdef.id] = actdef;
return {
op: 'next',
};
},
modify(spec) {
const actdef = spec.data.actdef;
const instance = spec.ctx.instance;
intern.deferred_modify_action(instance, actdef);
return {
op: 'next',
};
},
};
exports.task = task;
// TODO: write specific test cases for these
const intern = (module.exports.intern = {
make_action_stats: function (actdef) {
return {
id: actdef.id,
plugin: {
full: actdef.plugin_fullname,
name: actdef.plugin_name,
tag: actdef.plugin_tag,
},
prior: actdef.priorpath,
calls: 0,
done: 0,
fails: 0,
time: {},
};
},
// NOTE: use setImmediate so that action annotations (such as .validate)
// can be defined after call to seneca.add (for nicer plugin code order).
deferred_modify_action: function (seneca, actdef) {
setImmediate(function () {
(0, common_1.each)(seneca.private$.action_modifiers, function (actmod) {
actmod.call(seneca, actdef);
});
});
},
});
//# sourceMappingURL=add.js.map