mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Rework Function node module integration
This commit is contained in:
@@ -447,47 +447,5 @@ var api = module.exports = {
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets list of NPM modules
|
||||
* @param {Object} opts
|
||||
* @param {User} opts.user - the user calling the api
|
||||
* @param {Object} opts.req - the request to log (optional)
|
||||
* @return {Promise<Buffer>} - list of installed NPM modules
|
||||
* @memberof @node-red/runtime_nodes
|
||||
*/
|
||||
listNPMModules: async function(opts) {
|
||||
var promise = runtime.nodes.listNPMModules();
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Uninstall NPM modules
|
||||
* @param {Object} opts
|
||||
* @param {User} opts.user - the user calling the api
|
||||
* @param {Object} opts.req - the request to log (optional)
|
||||
* @return {Promise<Object>} - object for request result
|
||||
* @memberof @node-red/runtime_nodes
|
||||
*/
|
||||
uninstallNPMModule: async function(opts) {
|
||||
var spec = opts.spec;
|
||||
var promise = runtime.nodes.uninstallNPMModule(spec);
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update NPM modules
|
||||
* @param {Object} opts
|
||||
* @param {User} opts.user - the user calling the api
|
||||
* @param {Object} opts.req - the request to log (optional)
|
||||
* @return {Promise<Object>} - object for request result
|
||||
* @memberof @node-red/runtime_nodes
|
||||
*/
|
||||
updateNPMModule: async function(opts) {
|
||||
var spec = opts.spec;
|
||||
var isUpdate = opts.update;
|
||||
var promise = runtime.nodes.updateNPMModule(spec, isUpdate);
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
@@ -187,35 +187,35 @@ function setFlows(_config,_credentials,type,muteLog,forceStart,user) {
|
||||
});
|
||||
}
|
||||
|
||||
return configSavePromise
|
||||
.then(function(flowRevision) {
|
||||
if (!isLoad) {
|
||||
log.debug("saved flow revision: "+flowRevision);
|
||||
}
|
||||
activeConfig = {
|
||||
flows:config,
|
||||
rev:flowRevision
|
||||
};
|
||||
activeFlowConfig = newFlowConfig;
|
||||
if (forceStart || started) {
|
||||
// Flows are running (or should be)
|
||||
|
||||
// Stop the active flows (according to deploy type and the diff)
|
||||
return stop(type,diff,muteLog).then(() => {
|
||||
// Once stopped, allow context to remove anything no longer needed
|
||||
return context.clean(activeFlowConfig)
|
||||
}).then(() => {
|
||||
// Start the active flows
|
||||
start(type,diff,muteLog).then(() => {
|
||||
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
|
||||
});
|
||||
// Return the new revision asynchronously to the actual start
|
||||
return flowRevision;
|
||||
}).catch(function(err) { })
|
||||
} else {
|
||||
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
|
||||
}
|
||||
});
|
||||
return configSavePromise.then(flowRevision => {
|
||||
if (!isLoad) {
|
||||
log.debug("saved flow revision: "+flowRevision);
|
||||
}
|
||||
activeConfig = {
|
||||
flows:config,
|
||||
rev:flowRevision
|
||||
};
|
||||
activeFlowConfig = newFlowConfig;
|
||||
if (forceStart || started) {
|
||||
// Flows are running (or should be)
|
||||
|
||||
// Stop the active flows (according to deploy type and the diff)
|
||||
return stop(type,diff,muteLog).then(() => {
|
||||
// Once stopped, allow context to remove anything no longer needed
|
||||
return context.clean(activeFlowConfig)
|
||||
}).then(() => {
|
||||
// Start the active flows
|
||||
start(type,diff,muteLog).then(() => {
|
||||
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
|
||||
});
|
||||
// Return the new revision asynchronously to the actual start
|
||||
return flowRevision;
|
||||
}).catch(function(err) { })
|
||||
} else {
|
||||
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getNode(id) {
|
||||
@@ -246,7 +246,7 @@ function getFlows() {
|
||||
return activeConfig;
|
||||
}
|
||||
|
||||
function start(type,diff,muteLog) {
|
||||
async function start(type,diff,muteLog) {
|
||||
type = type||"full";
|
||||
started = true;
|
||||
var i;
|
||||
@@ -271,7 +271,21 @@ function start(type,diff,muteLog) {
|
||||
log.info(" "+settings.userDir);
|
||||
}
|
||||
events.emit("runtime-event",{id:"runtime-state",payload:{error:"missing-types", type:"warning",text:"notification.warnings.missing-types",types:activeFlowConfig.missingTypes},retain:true});
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await typeRegistry.checkFlowDependencies(activeConfig.flows);
|
||||
} catch(err) {
|
||||
log.info("Failed to load external modules required by this flow:");
|
||||
const missingModules = [];
|
||||
for (i=0;i<err.length;i++) {
|
||||
let errMessage = err[i].error.toString()
|
||||
missingModules.push({module:err[i].module.module, error: err[i].error.code || err[i].error.toString()})
|
||||
log.info(` - ${err[i].module.spec} [${err[i].error.code || "unknown_error"}]`);
|
||||
}
|
||||
events.emit("runtime-event",{id:"runtime-state",payload:{error:"missing-modules", type:"warning",text:"notification.warnings.missing-modules",modules:missingModules},retain:true});
|
||||
return;
|
||||
}
|
||||
|
||||
// In safe mode, don't actually start anything, emit the necessary runtime event and return
|
||||
@@ -280,7 +294,7 @@ function start(type,diff,muteLog) {
|
||||
log.info(log._("nodes.flows.safe-mode"));
|
||||
log.info("*****************************************************************")
|
||||
events.emit("runtime-event",{id:"runtime-state",payload:{error:"safe-mode", type:"warning",text:"notification.warnings.safe-mode"},retain:true});
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!muteLog) {
|
||||
@@ -370,7 +384,7 @@ function start(type,diff,muteLog) {
|
||||
log.info(log._("nodes.flows.started-flows"));
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
function stop(type,diff,muteLog) {
|
||||
|
@@ -26,7 +26,6 @@ var flows = require("../flows");
|
||||
var flowUtil = require("../flows/util")
|
||||
var context = require("./context");
|
||||
var Node = require("./Node");
|
||||
var npmModule = require("./npmModule");
|
||||
var log;
|
||||
|
||||
const events = require("@node-red/util").events;
|
||||
@@ -50,7 +49,6 @@ function registerType(nodeSet,type,constructor,opts) {
|
||||
type = nodeSet;
|
||||
nodeSet = "";
|
||||
}
|
||||
var dynModule = null;
|
||||
if (opts) {
|
||||
if (opts.credentials) {
|
||||
credentials.register(type,opts.credentials);
|
||||
@@ -62,11 +60,7 @@ function registerType(nodeSet,type,constructor,opts) {
|
||||
log.warn("["+type+"] "+err.message);
|
||||
}
|
||||
}
|
||||
if (opts.dynamicModuleList) {
|
||||
dynModule = opts.dynamicModuleList;
|
||||
}
|
||||
}
|
||||
npmModule.register(type, dynModule);
|
||||
if(!(constructor.prototype instanceof Node)) {
|
||||
if(Object.getPrototypeOf(constructor.prototype) === Object.prototype) {
|
||||
util.inherits(constructor,Node);
|
||||
@@ -87,7 +81,7 @@ function registerType(nodeSet,type,constructor,opts) {
|
||||
}
|
||||
}
|
||||
}
|
||||
registry.registerType(nodeSet,type,constructor);
|
||||
registry.registerType(nodeSet,type,constructor,opts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +110,6 @@ function createNode(node,def) {
|
||||
} else if (credentials.getDefinition(node.type)) {
|
||||
node.credentials = {};
|
||||
}
|
||||
return npmModule.checkInstall(def);
|
||||
}
|
||||
|
||||
function registerSubflow(nodeSet, subflow) {
|
||||
@@ -145,7 +138,6 @@ function init(runtime) {
|
||||
flows.init(runtime);
|
||||
registry.init(runtime);
|
||||
context.init(runtime.settings);
|
||||
npmModule.init(runtime);
|
||||
}
|
||||
|
||||
function disableNode(id) {
|
||||
@@ -270,10 +262,4 @@ module.exports = {
|
||||
loadContextsPlugin: context.load,
|
||||
closeContextsPlugin: context.close,
|
||||
listContextStores: context.listStores,
|
||||
|
||||
// NPM modules
|
||||
listNPMModules: npmModule.list,
|
||||
uninstallNPMModule: npmModule.uninstall,
|
||||
updateNPMModule: npmModule.update,
|
||||
loadNPMModule: npmModule.load
|
||||
};
|
||||
|
@@ -1,460 +0,0 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var path = require("path");
|
||||
var fs = require("fs-extra");
|
||||
var os = require("os");
|
||||
var util = require("@node-red/registry/lib/util");
|
||||
|
||||
var api;
|
||||
|
||||
var runtime;
|
||||
var settings;
|
||||
var exec;
|
||||
var log;
|
||||
|
||||
var npmCommand = (process.platform === "win32") ? "npm.cmd" : "npm";
|
||||
|
||||
var metadataFileName = "npm-modules.json";
|
||||
|
||||
var moduleProp = {};
|
||||
var moduleBase = null;
|
||||
|
||||
var allowInstall = true;
|
||||
var allowList = ["*"];
|
||||
var denyList = [];
|
||||
|
||||
var inProgress = {};
|
||||
|
||||
/**
|
||||
* Initialise npm install module.
|
||||
* @param {Object} _runtime - runtime object
|
||||
*/
|
||||
function init(_runtime) {
|
||||
runtime = _runtime;
|
||||
settings = _runtime.settings;
|
||||
exec = _runtime.exec;
|
||||
log = _runtime.log;
|
||||
|
||||
moduleProp = {};
|
||||
inProgress = {};
|
||||
|
||||
moduleBase = settings.userDir || process.env.NODE_RED_HOME || ".";
|
||||
|
||||
if (settings.hasOwnProperty("externalModules")) {
|
||||
var em = settings.externalModules;
|
||||
if (em && em.hasOwnProperty("modules")) {
|
||||
var mod = em.modules;
|
||||
if (mod.hasOwnProperty("allowInstall")) {
|
||||
allowInstall = mod.allowInstall;
|
||||
}
|
||||
if (mod.hasOwnProperty("allowList")) {
|
||||
var alist = mod.allowList;
|
||||
if (Array.isArray(alist)) {
|
||||
allowList = alist;
|
||||
}
|
||||
else {
|
||||
log.warn("unexpected value of externalModule.allowList in settings.js");
|
||||
}
|
||||
}
|
||||
if (mod.hasOwnProperty("denyList")) {
|
||||
var dlist = mod.denyList;
|
||||
if (Array.isArray(dlist)) {
|
||||
denyList = dlist;
|
||||
}
|
||||
else {
|
||||
log.warn("unexpected value of externalModule.denyList in settings.js");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register dynamic module installation property.
|
||||
* @param {string} type - node type
|
||||
* @param {string} prop - property name
|
||||
*/
|
||||
function register(type, prop) {
|
||||
if (prop) {
|
||||
moduleProp[type] = prop;
|
||||
}
|
||||
else {
|
||||
delete moduleProp[prop]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path to install modules
|
||||
*/
|
||||
function modulePath() { // takes variable length arguments in `arguments`
|
||||
var result = moduleBase;
|
||||
for(var i = 0; i < arguments.length; i++) {
|
||||
result = path.join(result, arguments[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompose NPM module specification string
|
||||
* @param {string} module - module specification
|
||||
* @return {Object} array [name, version], where name is name part and version is version part of the module
|
||||
*/
|
||||
function moduleName(module) {
|
||||
var match = /^([^@]+)@(.+)/.exec(module);
|
||||
if (match) {
|
||||
return [match[1], match[2]];
|
||||
}
|
||||
return [module, undefined];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get NPM module info
|
||||
* @param {string} name - module name
|
||||
* @return {Object} package.json for specified NPM module
|
||||
*/
|
||||
function infoNPM(name) {
|
||||
var path = modulePath("node_modules", name, "package.json");
|
||||
try {
|
||||
var pkg = fs.readFileSync(path);
|
||||
return JSON.parse(pkg);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load NPM module metadata
|
||||
* @return {object} module metadata object
|
||||
*/
|
||||
function loadMetadata() {
|
||||
var path = modulePath(metadataFileName);
|
||||
try {
|
||||
var pkg = fs.readFileSync(path);
|
||||
return JSON.parse(pkg);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
return {
|
||||
modules: []
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Save NPM module metadata
|
||||
* @param {string} data - module metadata object
|
||||
*/
|
||||
function saveMetadata(data) {
|
||||
var path = modulePath(metadataFileName);
|
||||
var str = JSON.stringify(data, null, 4);
|
||||
fs.writeFileSync(path, str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find item in metadata
|
||||
* @param {Object} meta - metadata
|
||||
* @param {string} name - module name
|
||||
* @return {object} metadata item
|
||||
*/
|
||||
function findModule(meta, name) {
|
||||
var modules = meta.modules;
|
||||
var item = modules.find(item => (item.name === name));
|
||||
return item;
|
||||
}
|
||||
|
||||
function setInProgress(name) {
|
||||
inProgress[name] = true;
|
||||
}
|
||||
|
||||
function clearInProgress(name) {
|
||||
inProgress[name] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install NPM module
|
||||
* @param {string} module - module specification
|
||||
*/
|
||||
function installNPM(module) {
|
||||
var [name, ver] = moduleName(module);
|
||||
setInProgress(name);
|
||||
return new Promise((resolve, reject) => {
|
||||
var pkg = infoNPM(name);
|
||||
if (!pkg) {
|
||||
var args = ["install", module];
|
||||
var dir = modulePath();
|
||||
return exec.run(npmCommand, args, {
|
||||
cwd: dir
|
||||
}, true).then(result => {
|
||||
if (result && (result.code === 0)) {
|
||||
pkg = infoNPM(name);
|
||||
var spec = name +(pkg ? "@"+pkg.version : "");
|
||||
log.info("successfully installed: "+spec);
|
||||
var meta = loadMetadata();
|
||||
var item = {
|
||||
name: name,
|
||||
spec: module,
|
||||
status: "installed",
|
||||
};
|
||||
meta.modules.push(item);
|
||||
saveMetadata(meta);
|
||||
clearInProgress(name);
|
||||
resolve(true);
|
||||
}
|
||||
else {
|
||||
clearInProgress(name);
|
||||
var msg = "failed to install: "+name;
|
||||
log.warn(msg);
|
||||
reject(msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
clearInProgress(name);
|
||||
var msg = "failed to install: "+name
|
||||
log.warn(msg);
|
||||
reject(msg);
|
||||
});
|
||||
}
|
||||
else {
|
||||
var meta = loadMetadata();
|
||||
if (!findModule(meta, name)) {
|
||||
var item = {
|
||||
name: name,
|
||||
spec: module,
|
||||
status: "preinstalled",
|
||||
};
|
||||
meta.modules.push(item);
|
||||
saveMetadata(meta);
|
||||
}
|
||||
clearInProgress(name);
|
||||
var spec = name +(pkg ? ("@"+pkg.version) : "");
|
||||
log.info("already installed: "+spec);
|
||||
}
|
||||
resolve(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check allowance of NPM module installation
|
||||
* @param {string} name - module specification
|
||||
*/
|
||||
function isAllowed(name) {
|
||||
if (!allowInstall) {
|
||||
return false;
|
||||
}
|
||||
var [module, ver] = moduleName(name);
|
||||
var aList = util.parseModuleList(allowList);
|
||||
var dList = util.parseModuleList(denyList);
|
||||
return util.checkModuleAllowed(module, ver, aList, dList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and install NPM module according to dynamic module specification
|
||||
* @param {Object} node - node object
|
||||
*/
|
||||
function checkInstall(node) {
|
||||
var name = null;
|
||||
if(moduleProp.hasOwnProperty(node.type)) {
|
||||
name = moduleProp[node.type];
|
||||
}
|
||||
var promises = [];
|
||||
if (name && node.hasOwnProperty(name)) {
|
||||
var modules = node[name];
|
||||
modules.forEach(module => {
|
||||
var name = module;
|
||||
if ((typeof module === "object") &&
|
||||
module &&
|
||||
module.hasOwnProperty("name")) {
|
||||
name = module.name;
|
||||
}
|
||||
if (isAllowed(name)) {
|
||||
var [n, v] = moduleName(name);
|
||||
setInProgress(name);
|
||||
promises.push(installNPM(name));
|
||||
}
|
||||
else {
|
||||
log.info("installation not allowed: "+name);
|
||||
}
|
||||
});
|
||||
}
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load NPM module
|
||||
* @param {string} module - module to load
|
||||
*/
|
||||
function load(module) {
|
||||
try {
|
||||
var [name, ver] = moduleName(module);
|
||||
var path = modulePath("node_modules", name);
|
||||
var npm = require(path);
|
||||
return npm;
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of installed modules
|
||||
*/
|
||||
function listModules() {
|
||||
return new Promise((resolve, reject) => {
|
||||
var meta = loadMetadata();
|
||||
var modules = meta.modules;
|
||||
modules.forEach(item => {
|
||||
var name = item.name;
|
||||
var info = infoNPM(name);
|
||||
if (info) {
|
||||
item.version = info.version;
|
||||
}
|
||||
item.inProgress = ((name in inProgress) && inProgress[name]);
|
||||
});
|
||||
Object.keys(inProgress).forEach(name => {
|
||||
if (inProgress[name] &&
|
||||
!modules.find(item => (item.name === name))) {
|
||||
modules.push({
|
||||
name: name,
|
||||
spec: name,
|
||||
state: "inprogress",
|
||||
inProgress: true
|
||||
});
|
||||
}
|
||||
});
|
||||
resolve(meta.modules);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall NPM modules
|
||||
*/
|
||||
function uninstall(module) {
|
||||
var [name, ver] = moduleName(module);
|
||||
setInProgress(name);
|
||||
return new Promise((resolve, reject) => {
|
||||
var pkg = infoNPM(name);
|
||||
var meta = loadMetadata();
|
||||
var item = findModule(meta, name);
|
||||
if (pkg && item) {
|
||||
if (item.status === "preinstalled") {
|
||||
clearInProgress(name);
|
||||
var msg = "can't uninstall preinstalled: "+name;
|
||||
log.warn(msg);
|
||||
reject(msg);
|
||||
}
|
||||
else {
|
||||
var args = ["uninstall", module];
|
||||
var dir = modulePath();
|
||||
return exec.run(npmCommand, args, {
|
||||
cwd: dir
|
||||
}, true).then(result => {
|
||||
if (result && (result.code === 0)) {
|
||||
log.info("successfully uninstalled: "+name);
|
||||
var meta = loadMetadata();
|
||||
var items = meta.modules.filter(item => (item.name !== name));
|
||||
meta.modules = items;
|
||||
saveMetadata(meta);
|
||||
clearInProgress(name);
|
||||
resolve(true);
|
||||
}
|
||||
else {
|
||||
clearInProgress(name);
|
||||
var msg = "failed to uninstall: "+name;
|
||||
log.warn(msg);
|
||||
reject(msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
clearInProgress(name);
|
||||
var msg = "failed to uninstall: "+name;
|
||||
log.warn(msg);
|
||||
reject(msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
clearInProgress(name);
|
||||
var msg = "module not installed: "+name;
|
||||
log.info(msg);
|
||||
reject(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update NPM modules
|
||||
*/
|
||||
function update(module, isUpdate) {
|
||||
var act = (isUpdate ? "updated": "install")
|
||||
var acted = (isUpdate ? "updated": "installed")
|
||||
var [name, ver] = moduleName(module);
|
||||
setInProgress(name);
|
||||
return new Promise((resolve, reject) => {
|
||||
var pkg = infoNPM(name);
|
||||
if (!pkg || isUpdate) {
|
||||
var args = ["install", module];
|
||||
var dir = modulePath();
|
||||
return exec.run(npmCommand, args, {
|
||||
cwd: dir
|
||||
}, true).then(result => {
|
||||
if (result && (result.code === 0)) {
|
||||
pkg = infoNPM(name);
|
||||
var spec = name +(pkg ? "@"+pkg.version : "");
|
||||
log.info("successfully "+acted+": "+spec);
|
||||
var meta = loadMetadata();
|
||||
var items = meta.modules.filter(item => (item.name !== name));
|
||||
var item = {
|
||||
name: name,
|
||||
spec: module,
|
||||
status: "installed",
|
||||
};
|
||||
items.push(item);
|
||||
meta.modules = items;
|
||||
saveMetadata(meta);
|
||||
clearInProgress(name);
|
||||
resolve(true);
|
||||
}
|
||||
else {
|
||||
clearInProgress(name);
|
||||
var msg = "failed to "+act+": "+name;
|
||||
log.warn(msg);
|
||||
reject(msg);
|
||||
}
|
||||
}).catch(e => {
|
||||
clearInProgress(name);
|
||||
var msg = "failed to "+act+": "+name;
|
||||
log.warn(msg);
|
||||
reject(msg);
|
||||
});
|
||||
}
|
||||
else {
|
||||
clearInProgress(name);
|
||||
var msg = "not "+acted+": "+name;
|
||||
log.info(msg);
|
||||
reject(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
api = {
|
||||
init: init,
|
||||
register: register,
|
||||
checkInstall: checkInstall,
|
||||
load: load,
|
||||
list: listModules,
|
||||
uninstall: uninstall,
|
||||
update: update
|
||||
};
|
||||
module.exports = api;
|
Reference in New Issue
Block a user