commit
919a54540f
|
@ -9,7 +9,8 @@
|
|||
"use strict";
|
||||
|
||||
var Basestar = require("./basestar"),
|
||||
Utils = require("./utils");
|
||||
Utils = require("./utils"),
|
||||
_ = require("./lodash");
|
||||
|
||||
// Public: Creates a new Adaptor
|
||||
//
|
||||
|
@ -33,11 +34,13 @@ var Adaptor = module.exports = function Adaptor(opts) {
|
|||
// misc. details provided in args hash
|
||||
this.details = {};
|
||||
|
||||
for (var opt in opts) {
|
||||
if (["robot", "name", "adaptor"].indexOf(opt) < 0) {
|
||||
this.details[opt] = opts[opt];
|
||||
}
|
||||
_.forEach(opts, function(opt, name) {
|
||||
if (_.include(["robot", "name", "adaptor"], opt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.details[name] = opt;
|
||||
}, this);
|
||||
};
|
||||
|
||||
Utils.subclass(Adaptor, Basestar);
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
"use strict";
|
||||
|
||||
var Registry = require("./registry"),
|
||||
Config = require("./config");
|
||||
Config = require("./config"),
|
||||
_ = require("./lodash");
|
||||
|
||||
var testMode = function() {
|
||||
return process.env.NODE_ENV === "test" && Config.testMode;
|
||||
|
@ -25,8 +26,7 @@ var testMode = function() {
|
|||
//
|
||||
// Returns the newly set-up connection
|
||||
module.exports = function Connection(opts) {
|
||||
var module,
|
||||
prop;
|
||||
var module;
|
||||
|
||||
opts = opts || {};
|
||||
|
||||
|
@ -43,24 +43,24 @@ module.exports = function Connection(opts) {
|
|||
|
||||
var adaptor = module.adaptor(opts);
|
||||
|
||||
for (prop in adaptor) {
|
||||
if (~["constructor"].indexOf(prop)) {
|
||||
continue;
|
||||
_.forEach(adaptor, function(prop, name) {
|
||||
if (name === "constructor") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof adaptor[prop] === "function") {
|
||||
adaptor[prop] = adaptor[prop].bind(adaptor);
|
||||
}
|
||||
if (_.isFunction(prop)) {
|
||||
adaptor[name] = prop.bind(adaptor);
|
||||
}
|
||||
});
|
||||
|
||||
if (testMode()) {
|
||||
var testAdaptor = Registry.findByAdaptor("test").adaptor(opts);
|
||||
|
||||
for (prop in adaptor) {
|
||||
if (typeof adaptor[prop] === "function" && !testAdaptor[prop]) {
|
||||
testAdaptor[prop] = function() { return true; };
|
||||
}
|
||||
_.forEach(adaptor, function(prop, name) {
|
||||
if (_.isFunction(prop) && !testAdaptor[name]) {
|
||||
testAdaptor[name] = function() { return true; };
|
||||
}
|
||||
});
|
||||
|
||||
return testAdaptor;
|
||||
}
|
||||
|
|
43
lib/cylon.js
43
lib/cylon.js
|
@ -13,7 +13,8 @@ var Async = require("async");
|
|||
var Logger = require("./logger"),
|
||||
Robot = require("./robot"),
|
||||
Config = require("./config"),
|
||||
Utils = require("./utils");
|
||||
Utils = require("./utils"),
|
||||
_ = require("./lodash");
|
||||
|
||||
var Cylon = module.exports = {
|
||||
Logger: Logger,
|
||||
|
@ -83,17 +84,13 @@ Cylon.api = function api(opts) {
|
|||
//
|
||||
// Returns nothing
|
||||
Cylon.start = function start() {
|
||||
var starters = [];
|
||||
for (var bot in this.robots) {
|
||||
starters.push(this.robots[bot].start);
|
||||
}
|
||||
var starters = _.map(this.robots, "start");
|
||||
|
||||
Async.parallel(starters, function() {
|
||||
var mode = Utils.fetch(Config, "workMode", "async");
|
||||
|
||||
if (mode === "sync") {
|
||||
for (var bot in this.robots) {
|
||||
this.robots[bot].startWork();
|
||||
}
|
||||
_.invoke(this.robots, "startWork");
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
@ -104,15 +101,13 @@ Cylon.start = function start() {
|
|||
//
|
||||
// Returns the current config
|
||||
Cylon.config = function(opts) {
|
||||
var loggingChanged = (opts.logging && Config.logging !== opts.logging);
|
||||
var logChanges = (opts.logging && !_.isEqual(Config.logging, opts.logging));
|
||||
|
||||
if (opts && typeof(opts) === "object" && !Array.isArray(opts)) {
|
||||
for (var o in opts) {
|
||||
Config[o] = opts[o];
|
||||
}
|
||||
if (_.isObject(opts) && !_.isArray(opts)) {
|
||||
Config = _.merge(Config, opts);
|
||||
}
|
||||
|
||||
if (loggingChanged) {
|
||||
if (logChanges) {
|
||||
Logger.setup();
|
||||
}
|
||||
|
||||
|
@ -126,30 +121,20 @@ Cylon.config = function(opts) {
|
|||
// Returns nothing
|
||||
Cylon.halt = function halt(callback) {
|
||||
callback = callback || function() {};
|
||||
|
||||
var fns = _.map(this.robots, "halt");
|
||||
|
||||
// if robots can"t shut down quickly enough, forcefully self-terminate
|
||||
var timeout = Config.haltTimeout || 3000;
|
||||
Utils.after(timeout, callback);
|
||||
|
||||
var fns = [];
|
||||
|
||||
for (var bot in this.robots) {
|
||||
var robot = this.robots[bot];
|
||||
fns.push(robot.halt.bind(robot));
|
||||
}
|
||||
|
||||
Async.parallel(fns, callback);
|
||||
};
|
||||
|
||||
Cylon.toJSON = function() {
|
||||
var robots = [];
|
||||
|
||||
for (var bot in this.robots) {
|
||||
robots.push(this.robots[bot].toJSON());
|
||||
}
|
||||
|
||||
return {
|
||||
robots: robots,
|
||||
commands: Object.keys(this.commands)
|
||||
robots: _.invoke(this.robots, "toJSON"),
|
||||
commands: _.keys(this.commands)
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
"use strict";
|
||||
|
||||
var Registry = require("./registry"),
|
||||
Config = require("./config");
|
||||
Config = require("./config"),
|
||||
_ = require("./lodash");
|
||||
|
||||
var testMode = function() {
|
||||
return process.env.NODE_ENV === "test" && Config.testMode;
|
||||
|
@ -26,8 +27,7 @@ var testMode = function() {
|
|||
//
|
||||
// Returns a new Device
|
||||
module.exports = function Device(opts) {
|
||||
var module,
|
||||
prop;
|
||||
var module;
|
||||
|
||||
if (opts.module) {
|
||||
module = Registry.register(opts.module);
|
||||
|
@ -44,24 +44,24 @@ module.exports = function Device(opts) {
|
|||
|
||||
var driver = module.driver(opts);
|
||||
|
||||
for (prop in driver) {
|
||||
if (~["constructor"].indexOf(prop)) {
|
||||
continue;
|
||||
_.forEach(driver, function(prop, name) {
|
||||
if (name === "constructor") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof driver[prop] === "function") {
|
||||
driver[prop] = driver[prop].bind(driver);
|
||||
}
|
||||
if (_.isFunction(prop)) {
|
||||
driver[name] = prop.bind(driver);
|
||||
}
|
||||
});
|
||||
|
||||
if (testMode()) {
|
||||
var testDriver = Registry.findByDriver("test").driver(opts);
|
||||
|
||||
for (prop in driver) {
|
||||
if (typeof driver[prop] === "function" && !testDriver[prop]) {
|
||||
testDriver[prop] = function() { return true; };
|
||||
}
|
||||
_.forEach(driver, function(prop, name) {
|
||||
if (_.isFunction(prop) && !testDriver[name]) {
|
||||
testDriver[name] = function() { return true; };
|
||||
}
|
||||
});
|
||||
|
||||
return testDriver;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
"use strict";
|
||||
|
||||
var Basestar = require("./basestar"),
|
||||
Utils = require("./utils");
|
||||
Utils = require("./utils"),
|
||||
_ = require("./lodash");
|
||||
|
||||
// Public: Creates a new Driver
|
||||
//
|
||||
|
@ -34,11 +35,13 @@ var Driver = module.exports = function Driver(opts) {
|
|||
|
||||
this.details = {};
|
||||
|
||||
for (var opt in opts) {
|
||||
if (["robot", "name", "connection", "driver"].indexOf(opt) < 0) {
|
||||
this.details[opt] = opts[opt];
|
||||
}
|
||||
_.forEach(opts, function(opt, name) {
|
||||
if (_.include(["robot", "name", "connection", "driver"], opt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.details[name] = opt;
|
||||
}, this);
|
||||
};
|
||||
|
||||
Utils.subclass(Driver, Basestar);
|
||||
|
@ -50,9 +53,7 @@ Driver.prototype.setupCommands = function(commands, proxy) {
|
|||
|
||||
this.proxyMethods(commands, proxy, this);
|
||||
|
||||
for (var i = 0; i < commands.length; i++) {
|
||||
var command = commands[i];
|
||||
|
||||
_.forEach(commands, function(command) {
|
||||
var snake_case = command.replace(/[A-Z]+/g, function(match) {
|
||||
if (match.length > 1) {
|
||||
match = match.replace(/[A-Z]$/, function(m) {
|
||||
|
@ -64,7 +65,7 @@ Driver.prototype.setupCommands = function(commands, proxy) {
|
|||
}).replace(/^_/, "");
|
||||
|
||||
this.commands[snake_case] = this[command];
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
Driver.prototype.toJSON = function() {
|
||||
|
@ -72,7 +73,7 @@ Driver.prototype.toJSON = function() {
|
|||
name: this.name,
|
||||
driver: this.constructor.name || this.name,
|
||||
connection: this.connection.name,
|
||||
commands: Object.keys(this.commands),
|
||||
commands: _.keys(this.commands),
|
||||
details: this.details
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
"use strict";
|
||||
|
||||
// loader for Lo-Dash.
|
||||
//
|
||||
// will load the compatability version on JS implementations with less support
|
||||
// for ES5 features (Tessel, etc)
|
||||
|
||||
var isTessel = function() {
|
||||
try {
|
||||
require("tessel");
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
if (isTessel()) {
|
||||
module.exports = require("lodash/dist/lodash.compat");
|
||||
} else {
|
||||
module.exports = require("lodash");
|
||||
}
|
148
lib/robot.js
148
lib/robot.js
|
@ -12,7 +12,8 @@ var initConnection = require("./connection"),
|
|||
initDevice = require("./device"),
|
||||
Logger = require("./logger"),
|
||||
Utils = require("./utils"),
|
||||
Config = require("./config");
|
||||
Config = require("./config"),
|
||||
_ = require("./lodash");
|
||||
|
||||
var Async = require("async"),
|
||||
EventEmitter = require("events").EventEmitter;
|
||||
|
@ -52,9 +53,7 @@ var Robot = module.exports = function Robot(opts) {
|
|||
"initConnections"
|
||||
];
|
||||
|
||||
methods.forEach(function(method) {
|
||||
this[method] = this[method].bind(this);
|
||||
}, this);
|
||||
_.bindAll(this, methods);
|
||||
|
||||
this.name = opts.name || Robot.randomName();
|
||||
this.connections = {};
|
||||
|
@ -72,35 +71,27 @@ var Robot = module.exports = function Robot(opts) {
|
|||
this.initConnections(opts);
|
||||
this.initDevices(opts);
|
||||
|
||||
for (var n in opts) {
|
||||
var opt = opts[n];
|
||||
|
||||
if (this[n] !== undefined) {
|
||||
continue;
|
||||
_.forEach(opts, function(opt, name) {
|
||||
if (this[name] !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this[n] = opt;
|
||||
this[name] = opt;
|
||||
|
||||
if (typeof opt === "function" && opts.commands == null) {
|
||||
this.commands[n] = opt;
|
||||
}
|
||||
if (opts.commands == null && _.isFunction(opt)) {
|
||||
this.commands[name] = opt;
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (opts.commands) {
|
||||
var cmds = opts.commands;
|
||||
var cmds = _.result(opts, "commands");
|
||||
|
||||
if (typeof cmds === "object") {
|
||||
if (_.isObject(cmds) && !_.isArray(cmds)) {
|
||||
this.commands = cmds;
|
||||
}
|
||||
|
||||
if (typeof cmds === "function") {
|
||||
var result = cmds.call(this, this);
|
||||
|
||||
if (typeof result === "object" && !Array.isArray(result)) {
|
||||
this.commands = result;
|
||||
} else {
|
||||
throw new Error("#commands function must return an object");
|
||||
}
|
||||
var err = "#commands must be an object ";
|
||||
err += "or a function that returns an object";
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,23 +116,11 @@ Robot.randomName = function() {
|
|||
//
|
||||
// Returns an Object containing Robot data
|
||||
Robot.prototype.toJSON = function() {
|
||||
var devices = [],
|
||||
connections = [],
|
||||
n;
|
||||
|
||||
for (n in this.connections) {
|
||||
connections.push(this.connections[n].toJSON());
|
||||
}
|
||||
|
||||
for (n in this.devices) {
|
||||
devices.push(this.devices[n].toJSON());
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
connections: connections,
|
||||
devices: devices,
|
||||
commands: Object.keys(this.commands)
|
||||
connections: _.invoke(this.connections, "toJSON"),
|
||||
devices: _.invoke(this.devices, "toJSON"),
|
||||
commands: _.keys(this.commands)
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -170,11 +149,9 @@ Robot.prototype.connection = function(name, conn) {
|
|||
//
|
||||
// Returns initialized connections
|
||||
Robot.prototype.initConnections = function(opts) {
|
||||
Logger.info("Initializing connections.");
|
||||
|
||||
var str;
|
||||
|
||||
var isArray = Array.isArray;
|
||||
Logger.info("Initializing connections.");
|
||||
|
||||
if (opts.connection == null && opts.connections == null) {
|
||||
return this.connections;
|
||||
|
@ -190,20 +167,17 @@ Robot.prototype.initConnections = function(opts) {
|
|||
return this.connections;
|
||||
}
|
||||
|
||||
if (typeof opts.connections === "object" && !isArray(opts.connections)) {
|
||||
for (var name in opts.connections) {
|
||||
this.connection(name, opts.connections[name]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isArray(opts.connections)) {
|
||||
if (_.isObject(opts.connections)) {
|
||||
if (_.isArray(opts.connections)) {
|
||||
str = "Specifying connections as an array is deprecated. ";
|
||||
str += "It will be removed in 1.0.0.";
|
||||
|
||||
Logger.warn(str);
|
||||
}
|
||||
|
||||
opts.connections.forEach(function(conn) {
|
||||
this.connection(conn.name, conn);
|
||||
_.forEach(opts.connections, function(conn, key) {
|
||||
var name = _.isString(key) ? key : conn.name;
|
||||
this.connection(name, conn);
|
||||
}, this);
|
||||
}
|
||||
|
||||
|
@ -234,10 +208,7 @@ Robot.prototype.device = function(name, device) {
|
|||
|
||||
device.connection = this.connections[device.connection];
|
||||
} else {
|
||||
for (var conn in this.connections) {
|
||||
device.connection = this.connections[conn];
|
||||
break;
|
||||
}
|
||||
device.connection = _.first(_.values(this.connections));
|
||||
}
|
||||
|
||||
this.devices[device.name] = initDevice(device);
|
||||
|
@ -251,8 +222,7 @@ Robot.prototype.device = function(name, device) {
|
|||
//
|
||||
// Returns initialized devices
|
||||
Robot.prototype.initDevices = function(opts) {
|
||||
var isArray = Array.isArray,
|
||||
str;
|
||||
var str;
|
||||
|
||||
Logger.info("Initializing devices.");
|
||||
|
||||
|
@ -274,18 +244,17 @@ Robot.prototype.initDevices = function(opts) {
|
|||
return this.devices;
|
||||
}
|
||||
|
||||
if (typeof opts.devices === "object" && !isArray(opts.devices)) {
|
||||
for (var name in opts.devices) {
|
||||
this.device(name, opts.devices[name]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isArray(opts.devices)) {
|
||||
if (_.isObject(opts.devices)) {
|
||||
if (_.isArray(opts.devices)) {
|
||||
str = "Specifying devices as an array is deprecated. ";
|
||||
str += "It will be removed in 1.0.0.";
|
||||
|
||||
Logger.warn(str);
|
||||
opts.devices.forEach(function(device) {
|
||||
this.device(device.name, device);
|
||||
}
|
||||
|
||||
_.forEach(opts.devices, function(device, key) {
|
||||
var name = _.isString(key) ? key : device.name;
|
||||
this.device(name, device);
|
||||
}, this);
|
||||
}
|
||||
|
||||
|
@ -304,25 +273,29 @@ Robot.prototype.start = function(callback) {
|
|||
|
||||
var mode = Utils.fetch(Config, "workMode", "async");
|
||||
|
||||
Async.series([
|
||||
this.startConnections,
|
||||
this.startDevices,
|
||||
function(callback) {
|
||||
var start = function() {
|
||||
if (mode === "async") {
|
||||
this.startWork();
|
||||
}
|
||||
callback(null, true);
|
||||
}.bind(this)
|
||||
}.bind(this);
|
||||
|
||||
Async.series([
|
||||
this.startConnections,
|
||||
this.startDevices,
|
||||
start
|
||||
], function(err, results) {
|
||||
if (!!err) {
|
||||
Logger.fatal("An error occured while trying to start the robot:");
|
||||
Logger.fatal(err);
|
||||
|
||||
if (typeof(this.error) === "function") {
|
||||
this.error.call(this, err);
|
||||
}
|
||||
|
||||
this.emit("error", err);
|
||||
}
|
||||
if (typeof(callback) === "function") {
|
||||
|
||||
if (_.isFunction(callback)) {
|
||||
callback(err, results);
|
||||
}
|
||||
}.bind(this));
|
||||
|
@ -351,11 +324,11 @@ Robot.prototype.startWork = function() {
|
|||
Robot.prototype.startConnections = function(callback) {
|
||||
Logger.info("Starting connections.");
|
||||
|
||||
var starters = Object.keys(this.connections).map(function(n) {
|
||||
var conn = this[n] = this.connections[n];
|
||||
var starters = _.map(this.connections, function(conn, name) {
|
||||
this[name] = conn;
|
||||
|
||||
return function(cb) {
|
||||
var str = "Starting connection '" + n + "'";
|
||||
var str = "Starting connection '" + name + "'";
|
||||
|
||||
if (conn.host) {
|
||||
str += " on host " + conn.host;
|
||||
|
@ -379,11 +352,11 @@ Robot.prototype.startConnections = function(callback) {
|
|||
Robot.prototype.startDevices = function(callback) {
|
||||
Logger.info("Starting devices.");
|
||||
|
||||
var starters = Object.keys(this.devices).map(function(n) {
|
||||
var device = this[n] = this.devices[n];
|
||||
var starters = _.map(this.devices, function(device, name) {
|
||||
this[name] = device;
|
||||
|
||||
return function(cb) {
|
||||
var str = "Starting device '" + n + "'";
|
||||
var str = "Starting device '" + name + "'";
|
||||
|
||||
if (device.pin) {
|
||||
str += " on pin " + device.pin;
|
||||
|
@ -407,19 +380,12 @@ Robot.prototype.startDevices = function(callback) {
|
|||
Robot.prototype.halt = function(callback) {
|
||||
callback = callback || function() {};
|
||||
|
||||
var fns = Object.keys(this.devices).map(function(d) {
|
||||
var device = this.devices[d];
|
||||
return device.halt.bind(device);
|
||||
}, this);
|
||||
var devices = _.map(this.devices, "halt");
|
||||
var connections = _.map(this.connections, "disconnect");
|
||||
|
||||
Async.parallel(fns, function() {
|
||||
var fns = Object.keys(this.connections).map(function(c) {
|
||||
var connection = this.connections[c];
|
||||
return connection.disconnect.bind(connection);
|
||||
}, this);
|
||||
|
||||
Async.parallel(fns, callback);
|
||||
}.bind(this));
|
||||
Async.parallel(devices, function() {
|
||||
Async.parallel(connections, callback);
|
||||
});
|
||||
|
||||
this.running = false;
|
||||
};
|
||||
|
|
43
lib/utils.js
43
lib/utils.js
|
@ -8,6 +8,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
var _ = require("./lodash");
|
||||
|
||||
var addCoreExtensions = function addCoreExtensions() {
|
||||
var max = Math.max,
|
||||
min = Math.min;
|
||||
|
@ -181,11 +183,9 @@ var Utils = module.exports = {
|
|||
this.constructor = child;
|
||||
};
|
||||
|
||||
for (var key in parent) {
|
||||
if (Object.hasOwnProperty.call(parent, key)) {
|
||||
child[key] = parent[key];
|
||||
}
|
||||
}
|
||||
_.forOwn(parent, function(prop, key) {
|
||||
child[key] = prop;
|
||||
});
|
||||
|
||||
Ctor.prototype = parent.prototype;
|
||||
child.prototype = new Ctor();
|
||||
|
@ -194,11 +194,11 @@ var Utils = module.exports = {
|
|||
},
|
||||
|
||||
proxyFunctions: function proxyFunctions(source, target) {
|
||||
for (var opt in source) {
|
||||
if (!target[opt] && typeof source[opt] === "function") {
|
||||
target[opt] = source[opt].bind(source);
|
||||
}
|
||||
_.forEach(source, function(prop, key) {
|
||||
if (_.isFunction(prop) && !target[key]) {
|
||||
target[key] = prop.bind(source);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Public: Proxies a list of methods from one object to another. It will not
|
||||
|
@ -216,25 +216,20 @@ var Utils = module.exports = {
|
|||
base = this;
|
||||
}
|
||||
|
||||
if (force == null) {
|
||||
force = false;
|
||||
force = force || false;
|
||||
|
||||
_.forEach(methods, function(method) {
|
||||
if (!_.isFunction(target[method])) {
|
||||
return;
|
||||
}
|
||||
|
||||
var proxy = function(method) {
|
||||
return base[method] = function() {
|
||||
return target[method].apply(target, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
for (var i = 0; i < methods.length; i++) {
|
||||
var method = methods[i];
|
||||
|
||||
if (!force && typeof(base[method]) === "function") {
|
||||
continue;
|
||||
if (_.isFunction(base[method]) && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
proxy(method);
|
||||
}
|
||||
base[method] = target[method].bind(target);
|
||||
});
|
||||
|
||||
return base;
|
||||
},
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
"async": "0.9.0",
|
||||
"express": "4.10.5",
|
||||
"body-parser": "1.10.0",
|
||||
"robeaux": "0.3.0"
|
||||
"robeaux": "0.3.0",
|
||||
"lodash": "2.4.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ describe("Robot", function() {
|
|||
it("throws an error", function() {
|
||||
expect(fn).to.throw(
|
||||
Error,
|
||||
"#commands function must return an object"
|
||||
"#commands must be an object or a function that returns an object"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue