Jelajahi Sumber

Initial commit

Luis Figueiredo 8 tahun lalu
melakukan
70d448e272
54 mengubah file dengan 2902 tambahan dan 0 penghapusan
  1. 1 0
      .gitignore
  2. 16 0
      bundles/hci-config/index.js
  3. 14 0
      bundles/hci-installer/hit-counter.js
  4. 81 0
      bundles/hci-installer/index.js
  5. 89 0
      bundles/hci-loader/clitable.js
  6. 142 0
      bundles/hci-loader/index.js
  7. 79 0
      bundles/hci-monitor/index.js
  8. TEMPAT SAMPAH
      bundles/hci-shell/.index.js.swp
  9. 59 0
      bundles/hci-shell/index.js
  10. 41 0
      doc/spec.md
  11. 4 0
      index.js
  12. 201 0
      lib/bundle-context.js
  13. 269 0
      lib/bundle-manager.js
  14. 244 0
      lib/xevents.js
  15. 24 0
      package.json
  16. 7 0
      test/hci-usecase/app/index.js
  17. 35 0
      test/hci-usecase/bundles/hci-http-monitor/controllers/bundles.js
  18. 37 0
      test/hci-usecase/bundles/hci-http-monitor/controllers/status.js
  19. 81 0
      test/hci-usecase/bundles/hci-http-monitor/index.js
  20. 15 0
      test/hci-usecase/bundles/hci-http-monitor/package.json
  21. TEMPAT SAMPAH
      test/hci-usecase/bundles/hci-http-monitor/web/.index.html.swp
  22. 45 0
      test/hci-usecase/bundles/hci-http-monitor/web/assets/main.js
  23. 52 0
      test/hci-usecase/bundles/hci-http-monitor/web/assets/style.css
  24. 36 0
      test/hci-usecase/bundles/hci-http-monitor/web/index.html
  25. 4 0
      test/hci-usecase/bundles/hci-http-monitor/web/vendor/jquery.min.js
  26. 56 0
      test/hci-usecase/bundles/hci-http/index.js
  27. 25 0
      test/hci-usecase/bundles/io-test/index.js
  28. 4 0
      test/hci-usecase/bundles/io-test/iobundles/test1/app/app.js
  29. 3 0
      test/hci-usecase/bundles/io-test/iobundles/test1/start.json
  30. 33 0
      test/hci-usecase/bundles/io-test/lib/IOActivator.js
  31. 42 0
      test/hci-usecase/bundles/memory-monitor/index.js
  32. 40 0
      test/hci-usecase/bundles/memory-tester/index.js
  33. TEMPAT SAMPAH
      test/hci-usecase/bundles/memory-tester/memory-test-unit/.index.js.swo
  34. 75 0
      test/hci-usecase/bundles/memory-tester/memory-test-unit/index.js
  35. 34 0
      test/hci-usecase/bundles/prettyjson/.jshintrc
  36. 3 0
      test/hci-usecase/bundles/prettyjson/.npmignore
  37. 6 0
      test/hci-usecase/bundles/prettyjson/.travis.yml
  38. 2 0
      test/hci-usecase/bundles/prettyjson/Authors.md
  39. 53 0
      test/hci-usecase/bundles/prettyjson/CONTRIBUTING.md
  40. 2 0
      test/hci-usecase/bundles/prettyjson/History.md
  41. 23 0
      test/hci-usecase/bundles/prettyjson/LICENSE
  42. 134 0
      test/hci-usecase/bundles/prettyjson/README.md
  43. 48 0
      test/hci-usecase/bundles/prettyjson/bin/prettyjson
  44. TEMPAT SAMPAH
      test/hci-usecase/bundles/prettyjson/images/example1.png
  45. TEMPAT SAMPAH
      test/hci-usecase/bundles/prettyjson/images/example2.png
  46. TEMPAT SAMPAH
      test/hci-usecase/bundles/prettyjson/images/example3.png
  47. TEMPAT SAMPAH
      test/hci-usecase/bundles/prettyjson/images/example4.png
  48. TEMPAT SAMPAH
      test/hci-usecase/bundles/prettyjson/images/example5.png
  49. 229 0
      test/hci-usecase/bundles/prettyjson/lib/prettyjson.js
  50. 25 0
      test/hci-usecase/bundles/prettyjson/lib/utils.js
  51. 105 0
      test/hci-usecase/bundles/prettyjson/package.json
  52. 348 0
      test/hci-usecase/bundles/prettyjson/test/prettyjson_spec.js
  53. 24 0
      test/hci-usecase/bundles/shell-test/index.js
  54. 12 0
      test/hci-usecase/package.json

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+node_modules

+ 16 - 0
bundles/hci-config/index.js

@@ -0,0 +1,16 @@
+var log = require('hlogger').createLogger('config');
+
+
+var configActivator = {
+	start(context) {
+	},
+	stop(context) {
+	},
+	test() {
+		console.log("Testing the configuration");
+	}
+}
+
+
+module.exports.bundleActivator = configActivator;
+

+ 14 - 0
bundles/hci-installer/hit-counter.js

@@ -0,0 +1,14 @@
+class HitCounter {
+	constructor(count,cb) {
+		this.count=count;
+		this.cb = cb;
+	}
+	hit() {
+		this.count--;
+		if(this.count == 0) {
+			this.cb();
+		}
+	}
+}
+
+module.exports = HitCounter;

+ 81 - 0
bundles/hci-installer/index.js

@@ -0,0 +1,81 @@
+var log = require('hlogger').createLogger("installer");
+var fs = require('fs');
+var path = require('path');
+var unzip = require('unzip');
+var exec = require('child_process').exec;
+
+var HitCounter = require('./hit-counter');
+
+
+var installerActivator = {
+
+	start(context) {
+
+		// With config
+		this.deployPath = context.manager.config.deployPath; 
+		this.runPath = context.manager.config.runPath;
+
+		if(this.deployPath == undefined) {
+			log.warn("There is no deploy path configured");
+			context.toggleOn('installed');	
+			return;
+		}
+		if(this.runPath == undefined) {
+			log.warn("There is no bundlePath configured");
+			context.toggleOn('installed');	
+			return;
+		}
+
+		var self = this;
+		log.info("Deploy: " + this.deployPath +", runPath: " + this.runPath);
+
+		var mods = fs.readdirSync(this.deployPath);
+
+		var hitCounter = new HitCounter(mods.length, () => {
+			log.info("Emitting installed");
+			context.toggleOn('installed',context);	
+		});
+
+		for(var v of mods) {
+			if(!v.endsWith(".zip")) {
+				log.warn("Ignoring file in deploy: " + v);
+				hitCounter.hit();
+				continue;
+			}
+			this.installPlugin(path.resolve( path.join(this.deployPath,v)), hitCounter.hit.bind(hitCounter));
+		}
+	},
+	installPlugin(fullPath,cb) {
+		log.info("Intalling: " + fullPath);
+		var basename = path.basename(fullPath,".zip");
+		log.info("Base name: " + basename);
+		var sourceZip = fullPath;
+		var modTargetPath = path.join(this.runPath,basename);
+		if(fs.existsSync(modTargetPath)) {
+			log.verb("Already exists ignoring");
+			if(!fs.existsSync(modTargetPath + "/node_modules")) {
+				installNpm(modTargetPath,cb);
+			} else {
+				cb();
+			}
+			return;
+		}
+
+		fs.mkdirSync(modTargetPath);
+
+		fs.createReadStream(sourceZip)
+			.pipe(unzip.Extract({path: modTargetPath}))
+			.on('close',() => {
+				this.installNpm(modTargetPath,cb);
+			});
+	},
+	installNpm(modTargetPath,cb) {
+		var child = exec("npm install", {cwd: modTargetPath});
+	  child.on('close',cb);
+		child.stdout.pipe(process.stdout);
+		child.stderr.pipe(process.stderr);
+	}
+
+}
+
+module.exports.bundleActivator = installerActivator;

+ 89 - 0
bundles/hci-loader/clitable.js

@@ -0,0 +1,89 @@
+
+function clitable(obj) {
+	var headers = {};
+	if(!obj instanceof Array) return;
+	// Gather headers
+	for(var row of obj) {
+		for(var col of Object.keys(row)) {
+			if(headers[col] == undefined) headers[col] = {maxLen: col.length};
+			headers[col].maxLen = Math.max(headers[col].maxLen,String(row[col]).length);	
+		}
+	}
+	
+	var tableLen = 0;
+	for(var k in headers) tableLen += headers[k].maxLen + 3;	
+
+	tableLen -=1;
+
+	var v = '│';
+	var h = '─';
+	var cul = '┌';
+	var cur = '┐';
+	var cdl = '└';
+	var cdr = '┘';
+	var cross = "┼";
+	
+	var crossD = "┬";
+	var crossU = "┴";
+	var crossR = "├";
+	var crossL = "┤";
+
+
+	var out="",c = 0;
+	// Top line
+	out += cul + h;	
+	c = 0;
+	for(var col in headers) {
+		out += ((c!=0)?h+ crossD + h:"") +h.repeat(headers[col].maxLen);
+		c++;
+	}
+	out += h + cur +"\n";
+
+
+	// Headers
+	out += v+" ";
+	for(var col in headers) {
+			// Pad Rest
+			var clen = headers[col].maxLen - (col.length);		
+			out += col + " ".repeat(clen) + " "+v+" ";
+		}
+	out+='\n';
+	// Middle line
+	out+=crossR+h;
+	var c = 0;
+	for(var col in headers) {
+		out += ((c!=0)?h+ cross + h:"") +h.repeat(headers[col].maxLen);
+		c++;
+	}
+	out += h + crossL +"\n";
+	//out += (v + h.times(tableLen) + v + "\n" );
+
+	for(var row of obj) { // check row len
+		out += v +" ";
+		for(var col in headers) {
+			var clen = headers[col].maxLen - String(row[col]).length;		
+
+			if(typeof(row[col]) == "number") {
+				out += " ".repeat(clen) +String(row[col]) +  " " + v +" ";
+
+			} else {
+				out += String(row[col]) + " ".repeat(clen) + " " + v +" ";
+			}
+		}
+		out +="\n";
+	}
+	
+	// Top line
+	out += cdl + h;	
+	c = 0;
+	for(var col in headers) {
+		out += ((c!=0)?h+ crossU + h:"") +h.repeat(headers[col].maxLen);
+		c++;
+	}
+	out += h + cdr +"\n";
+
+	return out;
+	
+}
+
+module.exports = clitable;

+ 142 - 0
bundles/hci-loader/index.js

@@ -0,0 +1,142 @@
+var log = require('hlogger').createLogger('loader');
+var path = require('path');
+var fs = require('fs');
+
+var clitable = require('./clitable');
+
+
+var loaderActivator = {
+	start(context) {
+		this.context = context;
+		this.manager = context.manager;
+
+		this.runPath = context.manager.config.runPath;
+		if(!this.runPath ) {
+			log.warn("There is no runPath configured in manager");
+			return;
+		}
+
+		context.on('hci-installer:installed',() => {
+			this.loadBundles();	
+		});
+
+		this.registerShell(context);
+
+	},
+	loadBundles() {
+		var mods = fs.readdirSync(this.runPath);
+		for(var v of mods) {
+			var modPath = path.join(this.runPath , v);
+			this.startPlugin(modPath);
+		}
+	},
+	startPlugin(plugPath) {
+		var dirparts = path.basename(plugPath).split(path.sep);
+		var dirname = dirparts[dirparts.length-1];
+
+		//var plugInfo = require(path.join(plugPath , "package.json"));
+		var plugName = dirname;
+		this.manager.register({name:plugName, bundle:plugPath});
+	},
+	registerShell(context) {
+		var prefix = context.prefix(".*:cmd");
+		prefix.on('lb',(args) => {
+			var tbl = [];
+			for(var k in context.manager.registry) {
+				var v = context.manager.registry[k];
+				if(v == undefined) {
+					continue;
+				}		
+				var modPath = v.modulePath;
+				if(modPath) {
+					modPath = modPath.replace(new RegExp("^" + process.env.PWD + "/"),"");
+				}
+				tbl.push({
+					id: v.id,
+					name:v.name,
+					state: v.info.state,
+					package: (v.info.pkgInfo)?v.info.pkgInfo.name:"",
+					version: (v.info.pkgInfo)?v.info.pkgInfo.version:"",
+					author: (v.info.pkgInfo)?v.info.pkgInfo.author.name:"",
+					modulePath: modPath,
+				});
+			}
+			tbl = tbl.sort((a,b) => { return a.id - b.id});
+			log.info("\n"+clitable(tbl));
+		});
+		prefix.on('stop',(args) => {
+			if(args.length < 2) {
+				log.error("Not enough parameters");
+				return;
+			}
+			var bcontext = context.manager.get(args[1]);
+			if(bcontext == null) {
+				log.error("Bundle not found");
+				return;
+			}
+			bcontext.stop();
+
+		});
+		prefix.on('start',(args) => {
+			if(args.length < 2) {
+				log.error("Not enough parameters");
+				return;
+			}
+			var bcontext = context.manager.get(args[1]);
+			if(bcontext == null) {
+				log.error("Bundle not found");
+				return;
+			}
+			bcontext.start();
+		});
+
+		prefix.on('reload',(args) => {
+			if(args.length < 2) {
+				log.error("Not enough parameters");
+				return;
+			}
+			var bcontext = context.manager.get(args[1]);
+			if(bcontext == null) {
+				log.error("Bundle not found");
+				return;
+			}
+			var modulePath = bcontext.modulePath;
+			
+			context.manager.unregister(bcontext);
+			context.manager.register({id:bcontext.id, name:bcontext.name, bundle:bcontext.modulePath});
+		});
+		prefix.on('unload',(args) => {
+			if(args.length < 2) {
+				log.error("Not enough parameters");
+				return;
+			}
+			var bcontext = context.manager.get(args[1]);
+			if(bcontext == null) {
+				log.error("Bundle not found");
+				return;
+			}
+			var modulePath = bcontext.modulePath;
+			
+			context.manager.unregister(bcontext);
+		});
+		
+		prefix.on('load',(args) => {
+			if(args.length < 2) {
+				log.error("Not enough parameters");
+				return;
+			}
+			log.info("Loading module from: " + process.env.PWD);
+			context.manager.load(process.env.PWD + "/" + args[1]);
+			//context.manager.register({name:args[1], bundle:path.resolve(path.join(process.env.PWD, args[2]))});
+		});
+	
+
+	}
+	
+
+
+
+};
+
+
+module.exports.bundleActivator = loaderActivator;

+ 79 - 0
bundles/hci-monitor/index.js

@@ -0,0 +1,79 @@
+var log = require('hlogger').createLogger("monitor");
+
+
+
+function ntokb(n) {
+	var suffix = ["B","KB","MB","GB"];
+	var si = 0;
+	while( n> 1024 && si< suffix.length) {
+		n/=1024;
+		si++;
+	}
+	return n.toFixed(2) + " " +suffix[si];
+}
+
+
+
+var monitor = {
+
+	bundleStart(context) {
+		this.context = context;
+		log.info("Bundle monitor started");
+		this.registerCommands(context);
+		context.on('hci-config', (config) => {
+			context.on(config,"start",function() {
+				config.test();
+			});	
+		});
+	},
+	registerCommands(context) {
+		// listen from command from ourselves
+		context.on('.*:cmd:stat',(args) => {
+			/*var r = {
+				numberOfModules: Object.keys(context.manager.registry).length,
+				numberOfListeners: context.manager.events.listeners.length,
+				listeners: context.manager.events.listeners.map((l) => l.name),
+				memory: process.memoryUsage()
+			}
+			context.with('prettyjson').do((pj) => {
+				log.info(pj.render(r));
+			});*/
+			this.showStat();
+		});
+		context.on('.*:cmd:gc',(args) => {
+			gc();
+		});
+	},
+	showStat() {
+		var context = this.context;
+		var obj = process.memoryUsage();
+		log.info("Memory Rss: " + ntokb(obj.rss)
+							 +" Total: " + ntokb(obj.heapTotal)
+							 +" Used: " + ntokb(obj.heapUsed));
+		log.info(
+			"Modules registered: " + context.manager.stat.modulesRegistered,
+			"Modules UnRegistered: " + context.manager.stat.modulesUnRegistered,
+			"events emitted: " + context.manager.events.stat.emitted,
+			"events called: " + context.manager.events.stat.called,
+			"events tried: " + context.manager.events.stat.tried
+		);
+	},
+	// Human readable stat
+	getStat() {
+		var obj = {};
+		var mem = process.memoryUsage();
+
+		obj.mem = {
+			rss: ntokb(mem.rss),
+			heapTotal: ntokb(mem.heapTotal),
+			heapUsed: ntokb(mem.heapUsed),
+		}
+		return obj;
+	}
+
+
+
+
+};
+
+module.exports = monitor;

TEMPAT SAMPAH
bundles/hci-shell/.index.js.swp


+ 59 - 0
bundles/hci-shell/index.js

@@ -0,0 +1,59 @@
+var readline = require('readline');
+var log = require('hlogger').createLogger("shell");
+var path = require('path');
+
+
+
+var shellActivator = {
+
+	start(context) {
+		this.context = context;
+		this.manager = context.manager;
+
+		this.stdout = process.stdout;
+		this.registerCommands(context);	
+		this.cli = readline.createInterface(process.stdin,this.stdout,(str) => this.completer(str));
+		this.cli.setPrompt("HCI> ");
+	
+		this.cli.on('line',(cmd) => {
+			if(cmd.length == "") {this.cli.prompt(); return;}
+			var args = cmd.split(/\s/);
+			context.emit("cmd:" + args[0],args).done(()=> {
+				this.cli.prompt();	
+			});
+		});
+
+		this.cli.prompt();
+	},
+	stop(context) {
+		log.info("Closing cli");
+		this.cli.close();
+	},
+	completer(str) {
+		var hits = this.manager.listeners
+			.filter((v)=> {
+				var re = new RegExp("^"+ this.context.name + ":cmd:" +str + ".*");
+				if(re.test(v.name)) {
+					return v;
+				}
+			}).map((v) => v.name.split(":")[2] + " ");
+
+		return [hits,str];
+	},
+	output(str) {
+		this.stdout.write(str + "\n");
+	},
+	registerCommands(context) {
+	}
+	
+}
+
+
+
+
+
+
+
+
+
+module.exports.bundleActivator = shellActivator;

+ 41 - 0
doc/spec.md

@@ -0,0 +1,41 @@
+
+
+# Request response idea:
+
+Module gets loaded, and request 
+
+Whats the time?
+context.call("clock:time",(res) {
+	// Any module using time will answer this
+});
+context.on("clock:time",(res) {
+	res(new Date()); // Will continue to next thing	
+});
+
+
+
+### Event flow:
+Reason why this can happen in the control event flow
+	Contexts are only allowed to send messages on their behalf:
+
+	Consider the module 'clock':
+		context.emit("time") -> will emit "clock:time"
+		context.emit("core-installer:installed") -> will emit "clock:core-installer:installed"
+	
+	Other module: expect a module named "clock" to send time
+		context.on("clock:time",function(args) {
+			var [time] = args;
+			console.log("Clock sent: " + time);
+
+		});
+
+Conclusion:
+		we cannot allow a module named 'clock' to fire events for core-installer
+
+#### How will modules call other modules methods
+	Since we can't send events to specific modules we can use the regular way:
+		context.with("clock").do((clock) => {
+			clock.time();
+		})
+
+

+ 4 - 0
index.js

@@ -0,0 +1,4 @@
+
+
+module.exports = require('./lib/bundle-manager');
+

+ 201 - 0
lib/bundle-context.js

@@ -0,0 +1,201 @@
+/**
+ * TODO here:
+ * Create try catch in start and stop: modules are not being unloaded/reloaded if there is exception in stop
+ * Improve fields, solve redundancy, on "on" and "after"
+ * Maybe use the XPrefix to create an endpoint named events or fetch a channel
+ * 	channel should work for emit aswell
+ *
+ * Separate vars in several contexts
+ *
+ * For the endpoint when bundle is stopped we could 
+ *
+ * */
+
+var log = require('hlogger').createLogger('bundle-context');
+
+/**
+ * Check method in bundleActivator if not check as bundleMethod
+ */
+function solveMethod(instance,name) {
+	if(instance.bundleActivator != undefined && instance.bundleActivator[name]!= undefined ) {
+		return (context) => {instance.bundleActivator[name](context)};
+	}
+	var subName = "bundle" + name[0].toUpperCase() + name.slice(1);
+	if(instance[subName] != undefined ) {
+		return (context) => {instance[subName](context)};
+	}
+	return null;
+}
+
+// Maybe create channel code here and completelly remove on here
+//
+
+// This will contain the listeners for Activator
+class BundleContext /*extends XEventEmitter*/ {
+	constructor(conf) {
+		/*super();*/
+		var info = {};
+		({id:info.id,
+			name:info.name,
+			manager:info.manager,
+			instance:info.instance,
+			modulePath:info.modulePath,
+			pkgInfo:info.pkgInfo} = conf);
+		//info = fromFields(["id","name","manager","instance","modulePath","pkgInfo"],conf);
+		info.state = 0;
+		info.loadDate = new Date();
+		
+		this.info = info;
+		this._bundleContext = {
+			intervals:[],
+			timeouts:[],
+			instanceStart: null,
+			instanceStop: null
+		};
+
+		var dummyFunc = () => {};
+		this._bundleContext.instanceStart = solveMethod(this.instance,"start") || dummyFunc;
+		this._bundleContext.instanceStop = solveMethod(this.instance,"stop") || dummyFunc;
+		
+	}	
+
+	get id() {
+		return this.info.id;
+	}
+	get modulePath() {
+		return this.info.modulePath;
+	}
+	get name() {
+		return this.info.name;
+	}
+	get instance() {
+		return this.info.instance;
+	}
+	get manager() {
+		return this.info.manager;
+	}
+	get version() {
+		return this.info.version;
+	}
+	start() {
+		if(this.info.state == 1) {
+			log.warn("Instance is turned on trying to turn off first");
+			this.stop();
+		}
+		process.nextTick(() => {
+			try {
+				this._bundleContext.instanceStart(this);
+			} catch(e) {
+				log.error("Bundle start error: " + this.name + "\n",e);
+			}
+			this.manager.events.toggleOn(this.name,this.instance);
+			this.info.state = 1;
+			this.info.startDate = new Date();
+		});
+	}
+
+	stop() {
+		if(this.info.state == 0) return;
+		this.toggleOff("start");
+		this.emit("stop");
+		try {
+			this._bundleContext.instanceStop(this);
+		} catch( e) {
+			log.error("Bundle stop error: " + this.name + "\n",e);
+		}
+
+		this.clearIntervals();
+		this.clearTimeouts();
+
+		this.manager.events.removeListener(this);
+		this.info.state = 0;
+	}
+
+	/* Event wrappers */
+	removeListener(ename) {
+		this.manager.events.removeListener(this,ename);
+	}
+	emit(ename,arg,cb) {
+		return this.manager.events.emit(this.name+":"+ename,arg,cb);
+	}
+	toggleOn(ename,arg) {
+		this.manager.events.toggleOn(this.name+":"+ename,arg);
+	}
+	toggleOff(ename) {
+		this.manager.events.toggleOff(this.name+":"+ename);
+	}
+	on(...args) {
+		var cb = args.pop();
+		var evtName = args.join(":");
+		this.manager.events.on(this,evtName ,cb);
+		return this;
+	}
+	after(...args) {
+		var cb = args.pop();
+		var evtName = args.join(":")
+		this.manager.events.after(this,evtName ,cb);
+		return this;
+	}
+	// Channel
+	prefix(name) {
+		return this.manager.events.prefix(this,name);
+	}
+	// Wrappers
+	
+	/**
+	 * @param {array} args 
+	 * @param {function} callback
+	 */
+	with(args,callback) {
+		return this.manager.with(args,callback);
+	}
+
+	/**
+	 * @param {string} match
+	 * @param {variable} ...args
+	 */
+	call(name,...args) {
+		return this.manager.call(name,args);
+	}
+
+	/*
+	get(name) {
+		var ctx = this.manager.get(name);
+		if(ctx == null || ctx.info.state != 1) { 
+			// Early bail
+			return null;
+		}
+		return ctx.instance;
+	}*/
+
+	// Auto clear helpers
+	//
+	setInterval(cb,n) {
+		var ret = setInterval(cb,n);
+		this._bundleContext.intervals.push(ret);
+		return ret;
+	}
+	clearIntervals() {
+		this._bundleContext.intervals.forEach((v) => {
+			clearInterval(v);
+		});
+	}
+	setTimeout(cb,n) {
+		var ret = setTimeout(cb,n);
+		this._bundleContext.timeouts.push(ret);
+		return ret;
+	}
+	clearTimeouts() {
+		this._bundleContext.timeouts.forEach((v) => {
+			clearTimeout(v);
+		});	
+	}
+
+
+}
+
+
+module.exports = BundleContext;
+
+
+

+ 269 - 0
lib/bundle-manager.js

@@ -0,0 +1,269 @@
+var log = require('hlogger').createLogger('bundle-manager');
+var path = require('path');
+var fs = require('fs');
+
+var BundleContext = require('./bundle-context');
+var XEventEmitter = require('./xevents');
+
+
+
+// Registry can be part of the package.json
+//
+
+function clearRequire(mod,resolve) {
+	
+	var modr = (resolve==undefined)?require.resolve(mod):mod;
+	var cache = require.cache[modr];
+
+	// Recursivelly unload children too
+	var cc = [...cache.children];
+	cc.forEach((c) => {
+		clearRequire(c.id,false);
+	});
+
+
+	var ci;
+	if((ci = cache.parent.children.indexOf(cache)) != -1) {
+		cache.parent.children.splice(ci,1);
+	}
+	delete require.cache[modr];
+}
+
+function getPackageJson(inpath) {
+	var pathParts = path.resolve(inpath).split(path.sep);
+
+	while(pathParts.length > 0) {
+		var fullPath = path.join("/", path.join.apply(path,pathParts),"/package.json"); 
+		if(fs.existsSync(fullPath)) {
+			return JSON.parse(fs.readFileSync(fullPath));
+		}
+		pathParts.pop();
+	}
+	return null;
+	
+}
+function solveBundleName(inpath) {
+	var name = path.basename(inpath);
+	if(name == "index.js") {
+		name = path.basename(path.dirname(inpath));
+	} else {
+		name = path.basename(inpath,".js");
+	}
+	return name;
+};
+
+
+// Common registry
+class BundleManager {
+	constructor(opts) {
+		// Control events	
+		this.config = opts || {};
+
+		this.events = new XEventEmitter();
+		// Global channel
+		this.globalEvents = new XEventEmitter();
+
+		this.stat = {
+			modulesRegistered:0,
+			modulesUnRegistered:0
+		};	
+		this.registry = {};	
+		//this.registry = [];
+	}
+	generateId() {
+		var id=0;
+		// Slow -- by fetching all ids everytime
+		do { id++; } while(this.get(id) != null); // Slow but one timer
+		return id;
+	}
+	loadDefaultBundles() {
+		var corePath = path.join(__dirname,"../bundles");
+		fs.readdir(corePath, (err,dir) => {
+			for( var file of dir ) {
+				this.load(path.join(corePath,file));
+			}
+		});
+	}
+	getBundleByInstance(instance) {
+		//return this.registry.find((el) => el.instance == instance);
+		for(var k in this.registry) {
+			if(this.registry[k].instance == instance) {
+				return this.registry[k];
+			}
+		}
+		return null;
+	}
+	getBundleById(id) {
+		//return this.registry.find((el) => el.id == id);
+		for(var k in this.registry) {
+			if(this.registry[k].id == id) {
+				return this.registry[k];
+			}
+		}
+		return null;
+	}
+	getBundle(name) {
+		//return this.registry.find((el) => el.name == name);
+		return this.registry[name];
+	}
+	get(obj) {
+		if(isNaN(obj) == false) {
+			return this.getBundleById(obj);
+		}
+		if(typeof(obj) == "string") {
+			return this.getBundle(obj);
+		}
+		if(obj instanceof BundleContext) {
+			return obj;
+		}
+		return this.getBundleByInstance(obj);
+	}
+	load(modPath) {
+		var modName = solveBundleName(modPath);
+		this.register({name: modName,bundle:modPath});	
+	}
+
+	register(opts) {
+		var {id,name,bundle,autostart} = opts;
+		
+		var instance;
+		var modulePath;
+		var mod = bundle;
+		var version = "unknown";
+
+		if(this.getBundle(name) != undefined) {
+			log.error(name + " Already exists");	
+			return;
+		}
+
+		if(typeof(bundle) == "string") {
+			var modPath= path.resolve(bundle);
+
+			var modInfo = getPackageJson(bundle);
+			try {
+				mod = require(modPath);
+			} catch(e) {
+				console.error(e);
+				return;
+			}
+			if(mod == undefined) {
+				log.error("Undefined module at: " + bundle);
+			}
+			modulePath = modPath;
+		} 
+		
+
+		/*if(mod.bundleActivator !=undefined ) {
+			instance = mod.bundleActivator;
+		} else {*/
+			if(mod.bundleFactory != undefined) {
+				instance = new mod.bundleFactory()
+			} else {
+				instance = mod;
+			}
+		/*}*/
+
+		id = id || this.generateId();
+		// Simplify this
+		var context = new BundleContext({
+																	id: id,
+																	name:name, 
+																	manager:this,
+																	instance: instance,
+																	modulePath:modulePath,
+																	pkgInfo: modInfo,
+																	version:version
+		});
+		
+		this.stat.modulesRegistered++;
+
+		this.registry[name] = context;
+		//this.registry.push(context);
+
+
+		if(autostart != false) {
+			context.start();
+		}
+
+	}
+	unregister(obj) {
+
+		var context = this.get(obj);
+		if(context == null) {
+			log.error("Context not found");
+			return;
+		}
+
+		context.stop();
+		// Unload require
+		if(context.modulePath != undefined) {	
+			clearRequire(context.modulePath);
+		}
+		/*this.registry = this.registry.filter((ctx) => {
+			return ctx != context;
+		});*/
+		delete this.registry[context.name];
+		this.stat.modulesUnRegistered++;
+	}
+
+	// Batch call
+	call(name,args) {
+		var [ctxName, method] = name.split(":")
+		var matchCtx = new RegExp(ctxName);
+		var fretList = [];
+		for(var ctxn in this.registry) {
+			var ctx = this.registry[ctxn];
+			if(!matchCtx.test(ctx.name)) {
+				continue;
+			}
+			var ret = { name:ctx.name}
+			if(ctx.instance[method] == undefined || typeof(ctx.instance[method]) != "function") {
+				ret.result = 0;
+				ret.error = "method not found";
+			} else {
+				ret.result = ctx.instance[method](args);
+			}
+			fretList.push(ret);
+		}
+		return fretList;
+	}
+
+	with(args,cb) {
+		var mods = args;
+		/*if(typeof(args[args.length-1]) != "function") {
+			throw new Error("Deprecated having callback on argument");
+		}*/
+		if(!Array.isArray(args)) {
+			mods = [args];
+		}
+		// Move this to manager directly
+		var iParam = [];
+
+		var ret = { // we could create generic out of this, its not a promise
+			success: true,
+			/*do: function(cb) {
+				if(this.success) cb.apply(cb,iParam);
+				return this;
+			},*/
+			else: function(cb) {
+				if(!this.success)cb();
+				return this;
+			}
+		}
+
+		for(var v of mods) {
+			if(v == undefined) throw new Error("What?");
+			var ctx = this.get(v);
+			if(ctx == null || ctx.info.state != 1) { 
+				ret.success = false;
+				break;
+			} // Early bail
+			iParam.push(ctx.instance);
+		}	
+		if(ret.success) cb(...iParam);
+		return ret;
+	}
+}
+
+
+module.exports = BundleManager;

+ 244 - 0
lib/xevents.js

@@ -0,0 +1,244 @@
+var log = require('hlogger').createLogger('xevents');
+
+
+class XPrefix {
+	constructor(ctx,event,prefix) {
+		this.ctx = ctx;
+		this.event = event;
+		this.prefix = prefix || ""; 
+	}
+	on(name,cb) {
+		this.event.on(this.ctx,this.prefix + ":" + name,cb);
+		return this;
+	}
+}
+
+
+
+class XEvent {
+	constructor(donecb,args) {
+		this.count = 0;
+		this.dowait = 0;
+		
+		this.done= donecb; // Callback
+		this.errors = [];
+		this.params = args;
+	}
+	wait() {
+		this.dowait = 1;
+	}
+}
+
+class XEventListener {
+	constructor(ctx,name,cb,order) {
+		this.ctx = ctx;
+		this.name = name;
+		this.callback = cb;
+		this.order = order || 0; // Late call
+	}
+}
+
+
+// Smarter event system?
+class XEventEmitter {
+	
+	constructor() {
+		// Prepare this in a single context
+
+		//this.listeners = new Map();
+		this.listeners = [];
+		this.auto = {};
+		this.stat = {
+			emitted:0,
+			tried:0,
+			called:0
+		}
+	}
+
+	addListener(...args) {
+		var ctx = this,evtName,cb,order;
+		if(typeof(args[0]) == "string") {
+			[evtName,cb,order] = args;
+		} else {
+			[ctx,evtName,cb,order] = args;
+		}
+
+		// Manipulate the evtName as in:
+		//
+	
+		/*var re = /[^\\]{(.*?)}/;
+		var paramMap = [];
+		do {	
+			var res = evtName.match(re)
+			if(res != null) {
+				paramMap.push(res[1]);
+				// The match + first char of the match
+				evtName = evtName.replace(re,res[0][0]+"(.*)");
+			}
+		}while(res != null);
+		console.log("Resulting evtName: " + evtName);*/
+
+		var listener = new XEventListener(ctx,evtName,cb,order);
+		//listener.paramMap = paramMap;
+
+
+
+		this.listeners.push(listener);
+		//this.listeners.push(entry);
+	
+		// Process the autos
+		var re = new RegExp(evtName);
+		Object.keys(this.auto).map((v) => {
+			var match = v.match(re);
+			if(v.match(re)) {
+				this.process([{match:match,listener:listener}],this.auto[v]);
+			}
+		});
+	
+			/*	try {
+					cb(this.auto[v], new XEvent());
+				} catch(e) {
+					console.log(e);
+				}
+			}
+		});*/
+	}
+	// Remove listeners by ctx
+	removeListener(ctx,name) {
+		this.listeners = this.listeners.filter((v) => {
+			if(v == ctx || v.ctx == ctx) {
+			} else {
+				return v;
+			}
+		});
+	}
+	
+	// 1 arg only
+	toggleOn(name,arg) {
+		this.auto[name]= arg;
+		this.emit(name,arg);
+	}
+
+	toggleOff(name) {
+		delete this.auto[name];
+	}
+
+	process(tocall,args,cb) {
+		var self=this;
+		
+		var evt = new XEvent(next,args);
+
+		this.stat.emitted++;
+		// Sort
+		tocall = tocall.sort((a,b) => {
+			return (a.listener.order - b.listener.order) 
+		});	
+		// Get iter and setup evt
+		var iter = tocall[Symbol.iterator]();
+		
+		function next() {
+			process.nextTick(() => chain(iter));
+		}
+
+		// Internal thing
+		function chain(iter) { 
+			// Stack overflow risk? maybe put this in process.tick();
+			var v = iter.next();
+			if(v.done) {
+				if(cb != undefined) cb(evt);
+				return;
+			}
+			// Create evt here with some global event chaining iteraction	
+			try {
+				var entry = v.value;
+				evt.match = entry.match;
+				evt.args = args;
+				self.stat.called++;
+				entry.listener.callback(args,evt);
+				evt.count++;
+			}catch(e) {
+				console.log(e);
+				evt.errors.push(e);
+				// Should we stop??
+			}
+			if(!evt.dowait) next();
+			evt.dowait=0; // reset wait?
+		}
+		next();
+	}
+
+	search(name,ctx) {
+		var ret = [];
+		this.listeners.forEach((v) => {
+			var re = new RegExp( "^" + v.name+ "$" );
+			var matchres = name.match(re);
+			if(matchres && (ctx == undefined || ctx == v.ctx) ) {
+				ret.push({
+					match: matchres,
+					listener:v
+				});
+			}
+		});
+		return ret;
+	}
+
+	emit(name,args) {
+		this.stat.tried++;
+		var tocall = this.search(name);
+		// Mem leak???
+		var ret = {
+			param: null,
+			callback: null,
+			// Setup callback
+			trigger: function(args,evt) {
+				this.param = [args,evt];
+				this.dotrigger = 1;
+				if(this.callback) {this.callback(args,evt);}
+			},
+			done: function(cb) {
+				if(this.dotrigger) {
+					cb(...param); 
+				}
+				this.callback = cb;
+			}
+		}
+		// Test
+		this.process(tocall,args,(evt) => { ret.trigger(args,evt) });
+		return ret ;
+	}
+	// Why?
+	emitTo(ctx,name,args) {
+		var tocall = this.search(name,ctx)
+		this.process(tocall,arg,cb);
+	}
+	// Alias
+	on(ctx,match,cb) {
+		this.addListener(ctx,match,cb);
+		return this;
+	}
+	after(ctx,match,cb) {
+		this.addListener(ctx,match,cb,1000);
+		return this;
+	}
+
+	prefix(ctx,prefix) {
+		return new XPrefix(ctx,this,prefix);
+	}
+	/*when(ctx,match,cb) {
+		this.addListener(ctx,match,cb);
+	}*/
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+module.exports=XEventEmitter;

+ 24 - 0
package.json

@@ -0,0 +1,24 @@
+{
+  "name": "@hexasoftware/hci",
+  "version": "0.0.6",
+  "description": "Hexa Continuous Integration",
+  "main": "index.js",
+  "author": {
+    "name": "Luis Figueiredo",
+    "email": "luisf@hexasoftware.com"
+  },
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [
+    "osgi",
+    "plugins",
+    "manager",
+    "framework"
+  ],
+  "license": "ISC",
+  "dependencies": {
+    "hlogger": "0.0.9",
+    "unzip": "^0.1.11"
+  }
+}

+ 7 - 0
test/hci-usecase/app/index.js

@@ -0,0 +1,7 @@
+var BundleManager = require('../../../');
+
+
+var manager = new BundleManager({runPath: __dirname + "/../bundles"});
+
+
+manager.loadDefaultBundles();

+ 35 - 0
test/hci-usecase/bundles/hci-http-monitor/controllers/bundles.js

@@ -0,0 +1,35 @@
+
+
+module.exports ={ 
+	index(context,done) {
+		return this.list(context,done)
+	},
+	list(context,done) {
+		var tbl = [];
+		for(var k in context.manager.registry) {
+			var v = context.manager.registry[k];
+			if(v == undefined) {
+				continue;
+			}		
+			tbl.push({
+				id: v.id,
+				name:v.name,
+				state: v.info.state,
+				package: (v.info.pkgInfo)?v.info.pkgInfo.name:"",
+				version: (v.info.pkgInfo)?v.info.pkgInfo.version:"",
+				author: ((v.info.pkgInfo)?v.info.pkgInfo.author.name:"") || "?", 
+				modulePath: v.modulePath,
+			});
+		}
+		tbl = tbl.sort((a,b) => { return a.id - b.id});
+
+		done(tbl);
+	}
+
+
+
+
+
+
+
+}

+ 37 - 0
test/hci-usecase/bundles/hci-http-monitor/controllers/status.js

@@ -0,0 +1,37 @@
+
+console.log("Reloading controller");
+
+module.exports = {
+
+	memory(context,done) {
+		// Wait call?
+		//
+		// Get first result	
+		var [ret] = context.manager.call("hci-monitor:getStat");
+		done(ret.result);
+		
+	},
+	bundles(context,done) {
+		var tbl = [];
+		for(var k in context.manager.registry) {
+			var v = context.manager.registry[k];
+			if(v == undefined) {
+				continue;
+			}		
+			tbl.push({
+				id: v.id,
+				name:v.name,
+				state: v.info.state,
+				package: (v.info.pkgInfo)?v.info.pkgInfo.name:"",
+				version: (v.info.pkgInfo)?v.info.pkgInfo.version:"",
+				author: ((v.info.pkgInfo)?v.info.pkgInfo.author.name:"") || "?", 
+				modulePath: v.modulePath,
+			});
+		}
+		tbl = tbl.sort((a,b) => { return a.id - b.id});
+
+		done(tbl);
+	}
+
+
+}

+ 81 - 0
test/hci-usecase/bundles/hci-http-monitor/index.js

@@ -0,0 +1,81 @@
+var log = require('hlogger').createLogger('hci-http-test');
+var mime = require('mime-types');
+var path = require('path');
+var fs = require('fs');
+
+function loadControllers() {
+	var targetPath = __dirname + "/controllers";
+	var files = fs.readdirSync(targetPath);
+	var ret = {}
+	for(var f of files) {
+		if(!f.endsWith(".js")) continue;
+		var filePath = path.join(targetPath, f);	
+		var controllerName = path.basename(f,".js");
+		ret[controllerName] = require(filePath);
+	};
+	return ret;
+}
+
+
+function bundleStart(context) {
+	var controllers = loadControllers();
+
+	var channel = context.prefix('hci-http:req');
+
+	channel.on('/monitor/api/(.*)',([req,res],e) => {
+
+		var [conName,conMethod = 'index',...params] = e.match[1].split(/\//);	
+		var con = controllers[conName];
+		if(con == undefined || con[conMethod] == undefined) {
+			res.statusCode = 404;
+			return;
+		}
+		var to = setTimeout(function() {
+			res.writeHead(408);
+			res.end("Controller timeout");
+		},5000);
+		function cb(conRet) {
+			clearTimeout(to);
+			res.end(JSON.stringify(conRet));
+		}
+		
+		con[conMethod](context,cb,...params);
+		// Setting a timeout would be good
+	});
+
+	// Static server for path / monitor
+	channel.on('/monitor/?(.*)',([req,res],e) => {
+		if(e.count != 0) { // Something else executed
+			return;
+		}
+		// Not executed
+		//
+		var reqroute = e.match[1];
+		if(reqroute.length == 0) {
+			reqroute = "index.html";
+		}
+		var filePath = path.join(__dirname, "/web");
+
+		var fullPath = path.join(filePath, reqroute);
+		if( fullPath.indexOf(filePath) != 0 ) {
+			res.writeHead(400,"Permission denied");
+			res.end("Permission denied");
+			return;
+		}
+		if(!fs.existsSync(fullPath)) {
+			res.statusCode = 404;
+			return;
+		}
+
+		// MimeTypes??
+		
+		var mimeType = mime.lookup(fullPath);
+		res.writeHead(200,{'Content-type':mimeType});
+		var s = fs.createReadStream(fullPath);
+		s.pipe(res);
+	});
+
+
+}
+
+module.exports.bundleStart = bundleStart;

+ 15 - 0
test/hci-usecase/bundles/hci-http-monitor/package.json

@@ -0,0 +1,15 @@
+{
+  "name": "hci-http-monitor",
+  "version": "0.0.3",
+  "description": "",
+  "main": "index.js",
+  "author": "",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [],
+  "license": "ISC",
+  "dependencies": {
+    "mime-types": "^2.1.12"
+  }
+}

TEMPAT SAMPAH
test/hci-usecase/bundles/hci-http-monitor/web/.index.html.swp


+ 45 - 0
test/hci-usecase/bundles/hci-http-monitor/web/assets/main.js

@@ -0,0 +1,45 @@
+$(function() {
+
+
+
+	setInterval(function() {
+		$.getJSON('/monitor/api/status/memory',function(data) {
+			console.log("Response: " + data);
+
+			if(data.mem != undefined) {
+				$('.mem-rss').html(data.mem.rss);
+				$('.mem-heap').html(data.mem.heapUsed + " / " + data.mem.heapTotal);
+			}
+		});
+
+	},2000);
+
+	$.getJSON('/monitor/api/bundles',function(data) {
+
+			$thead = $('table.bundle-list thead');
+			$thead.html('');
+			var add  = '';
+			add+= '<tr>';
+			Object.keys(data[0]).forEach(function(v) {
+				add+='<th>' + v + '</th>';
+			});
+			add +='</tr>'
+			console.log("Adding: " + add);
+			$thead.html(add);
+
+
+			$tbody = $('table.bundle-list tbody');
+			$tbody.html('');
+
+			data.forEach(function(v) {
+				add='<tr>'
+				Object.keys(v).forEach(function(e) {
+					add+='<td>&nbsp;' + v[e] + '</td>';
+				});
+				add+='</tr>';
+				$tbody.append(add);
+			});
+		});
+
+
+});

+ 52 - 0
test/hci-usecase/bundles/hci-http-monitor/web/assets/style.css

@@ -0,0 +1,52 @@
+@import url('https://fonts.googleapis.com/css?family=Arimo');
+
+body {
+	font-family: 'Arimo', sans-serif;
+}
+
+pane {
+	border: solid 1px rgba(0,0,0,0.3);
+	display:flex;
+	flex-direction: column;
+	padding:20px;
+	margin-bottom:20px;
+}
+pane > h3 {
+	margin:0px;
+	margin-bottom: 10px;
+}
+
+.control > label {
+	text-align:right;
+}
+
+
+table {
+	width:100%;
+	border-collapse: collapse;
+}
+
+table thead {
+	border-bottom: solid 2px rgba(0,0,0,0.2);
+}
+table thead tr th {
+	padding-bottom:10px;
+}
+table tbody tr{
+	cursor:pointer;
+	background: rgba(0,0,0,0);
+	transition: all 0.3s;
+}
+table tbody tr:hover {
+	background: rgba(150,200,200,0.3);
+	transition: all 0.3s;
+
+}
+table tbody tr td {
+	padding:12px;	
+	text-align:center;
+}
+
+
+
+

+ 36 - 0
test/hci-usecase/bundles/hci-http-monitor/web/index.html

@@ -0,0 +1,36 @@
+<html>
+	<head>
+		<title>HCI monitor</title>
+		<link rel='stylesheet' href='assets/style.css'>
+		<script src='vendor/jquery.min.js'></script>
+		<script src='assets/main.js'></script>
+
+	</head>
+	<body>
+		<pane>
+			<h3>Memory status</h3>
+			<content>
+				<div class='control'>
+					<label>Rss:</label>
+					<content class='mem-rss'></content>
+				</div>
+				<div class='control'>
+					<label>Heap:</label>
+					<content class='mem-heap'></content>
+				</div>
+			</content>
+		</pane>	
+
+		<pane>
+			<h3>Bundle status</h3>
+			<content>
+				<table class='bundle-list'>
+					<thead>
+					</thead>
+					<tbody>
+					</tbody>
+				</table>
+			</content>
+		</pane>
+	</body>
+</html>

File diff ditekan karena terlalu besar
+ 4 - 0
test/hci-usecase/bundles/hci-http-monitor/web/vendor/jquery.min.js


+ 56 - 0
test/hci-usecase/bundles/hci-http/index.js

@@ -0,0 +1,56 @@
+const http = require('http');
+const log = require('hlogger').createLogger('hci-http');
+
+var activator = {
+	start(context) {
+		this.server = http.createServer((req,res) => {
+
+			context.emit('req:'+req.url,[req,res]) //??
+						 .done(([req,res],evt) => {
+							 // Array of results
+							 if(res.statusCode == 404 || evt.count == 0) {
+									res.writeHead(404,"Not found");
+									res.end("Not found");
+								}	
+							});
+
+			// Setup stuff here to routing
+		});
+		// 
+		log.info("Server started at: " + 3500);
+		this.server.listen(3500);
+		
+		this.clients = {};
+
+		this.server.on('connection',(conn) => {
+			var key = conn.remoteAddress + ':' + conn.remotePort;
+			this.clients[key] = conn;
+			conn.on('close',() => {
+				delete this.clients[key];
+			});
+		});
+		
+		this.server.on('close',() => {
+			log.info("Http server went down");
+			for(var k in this.clients) {
+				this.clients[k].destroy();
+			};
+		});
+		context.toggleOn('serverStart',this);
+	},
+
+	stop(context) {
+		log.info("Server shutting down");
+		context.toggleOff('serverStart');
+		context.emit('serverStop');		
+		this.server.close()		// Shutdown clients
+		for(var k in this.clients) {
+				this.clients[k].destroy();
+		};
+
+		//this.server.emit('user:close');
+	}
+}
+
+
+module.exports.bundleActivator = activator;

+ 25 - 0
test/hci-usecase/bundles/io-test/index.js

@@ -0,0 +1,25 @@
+var fs = require('fs');
+var path = require('path');
+var IOActivator = require('./lib/IOActivator');
+var log = require('hlogger').createLogger('io-test');
+
+var activator = {
+	start(context) {
+		var files = fs.readdirSync(path.join(__dirname, "iobundles"));
+		for(var f of files) {
+			log.info("There is: " + f);
+		}
+	}
+}
+
+
+
+
+
+
+
+
+
+
+
+module.exports.bundleActivator = activator;

+ 4 - 0
test/hci-usecase/bundles/io-test/iobundles/test1/app/app.js

@@ -0,0 +1,4 @@
+
+
+
+console.log("Hello");

+ 3 - 0
test/hci-usecase/bundles/io-test/iobundles/test1/start.json

@@ -0,0 +1,3 @@
+{
+	"command":"node app"
+}

+ 33 - 0
test/hci-usecase/bundles/io-test/lib/IOActivator.js

@@ -0,0 +1,33 @@
+var fs= require('fs');
+var path = require('path');
+
+
+class IOActivator {
+	
+	constructor(iopath) {
+		var startJSON = path.join(iopath,"start.json");
+		this.procName = JSON.parse(fs.readFileSync(startJSON));
+	}
+	start(context) {
+		this.proc = child.exec(this.procName);	
+		// Write to
+		this.proc.stdout.on('data',(data) => {
+			//this.iChunk.push(data);
+			this.process(data);
+		});
+
+	}
+	process(data) {
+		// Assume json right away "try"
+		var msg = JSON.parse(data);
+		if(msg == 'emit') {
+			this.context.emit(msg.content).done((e) => {
+				log.info("Processed: " + e.count);
+			});	
+		}
+	}
+	stop(context) {
+		this.proc.kill('SIGINT');
+	}
+
+}

+ 42 - 0
test/hci-usecase/bundles/memory-monitor/index.js

@@ -0,0 +1,42 @@
+var log = require('hlogger').createLogger('memory-monitor');
+
+var activator = {
+	start(context) {
+
+		context
+			.prefix('core-shell:cmd')
+			.on('memory',([,sub,time]) => {
+				if(sub == "start") {
+					var itime = time || itime;
+					context.setInterval(() => {
+						context.call('core-monitor:showStat');
+					},itime);
+				}
+			}).on('cache',([,search])=> {
+				if(search) {
+					Object.keys(require.cache).forEach((v) => {
+						var re = new RegExp(search);
+						if(re.test(v)) {
+							log.info("Found module: " + v + " parent: " , v.parent);
+						}
+						var req = require.cache[v];
+						if(req.parent && re.test(req.parent.id)	) {
+							log.info("Found in parent: " + v + " - " + req.parent.id);
+						}
+						req.children.forEach((c) => {
+							if(re.test(c.id)) {
+								log.info("Found on child of: " +v + " - " + c.id);
+							}
+						});
+					});
+					return;
+				}
+			});
+
+	},
+	stop(context) {
+		clearInterval(this.interval);
+	}
+}
+
+module.exports.bundleActivator = activator;

+ 40 - 0
test/hci-usecase/bundles/memory-tester/index.js

@@ -0,0 +1,40 @@
+var log = require('hlogger').createLogger('monitor-test');
+
+function ntokb(n) {
+	var suffix = ["B","KB","MB","GB"];
+	var si = 0;
+	while( n> 1024 && si< suffix.length) {
+		n/=1024;
+		si++;
+	}
+	return n.toFixed(2) + " " +suffix[si];
+}
+
+
+var activator = {
+	start(context) {
+		context.on('core-shell:cmd:test',() => {
+			this.startTest(context);
+		});
+	},
+	startTest(context) {
+		log.info("Starting memory-test");
+		var btoggle = false;
+
+		context.setInterval(function() {
+			if(btoggle == false ) {
+				context.manager.load(__dirname + "./memory-test-unit");
+			}else {
+				context.manager.unregister("memory-test-unit");
+			}
+			btoggle = !btoggle;
+		},500);
+		
+	},
+	stop(context) {
+	}
+}
+
+module.exports.bundleActivator = activator;
+
+

TEMPAT SAMPAH
test/hci-usecase/bundles/memory-tester/memory-test-unit/.index.js.swo


+ 75 - 0
test/hci-usecase/bundles/memory-tester/memory-test-unit/index.js

@@ -0,0 +1,75 @@
+var log = require('hlogger').createLogger('jspak.Config');
+
+class BigObject {
+	constructor() {
+		this.temp = new Array(1e5);
+	}
+}
+
+class BigObjectContainer {
+	constructor() {
+		this.holder = [];
+	}
+}
+
+// Can be inside the thing too
+//var memTest = [];
+var activator = {
+	start(context) {
+		var memTest = new BigObjectContainer();
+		this.memTest = memTest;
+		for(var i = 0;i<50;i++) {
+			this.increaseMemory();
+		}
+
+		context.on('core-shell:cmd:ls',(cmd,e) => {
+			e.wait();
+			context.with(["mod1","prettyjson"],(mod1,pj) => {
+				log.info("Doing ls, we retrieved stuff");
+				log.info(pj.render({name:'test'}));
+			});
+			e.done();
+		});
+
+		var ocount = 1000;
+		var count = ocount;
+		for(var i = 0;i<ocount;i++) {
+			context.on('core-shell:cmd:mtest',(cmd,e) => {
+				e.wait();
+				if(count== ocount) {
+					context.emit("time");
+				}
+				count--;
+				if(count == 0) {
+					console.log("Everything called");
+					count = ocount;
+					context.emit("timeEnd");
+				}
+				e.done();
+			});
+		}
+		context.on(context,"time",() => {
+			console.time("beef");
+		});
+		context.on(context,"timeEnd",() => {
+				console.timeEnd("beef");
+		});
+		/*this.interval = setInterval(() => {
+			this.increaseMemory();
+		},100);*/
+	},
+	stop(context) {
+		//clearInterval(this.interval);
+		//this.memTest = null;
+	},
+	increaseMemory() {
+		this.memTest.holder.push(new BigObject());
+	//	console.log("Config memTest: " + this.memTest.holder.length + " " + process.memoryUsage().heapUsed);
+		//memTest.push(new Array(1e5));
+		//memTest.push(new BigObject());
+		//console.log("Config memTest: " + memTest.length + " " + process.memoryUsage().heapUsed);
+	}
+}
+
+module.exports.bundleActivator = activator;
+

+ 34 - 0
test/hci-usecase/bundles/prettyjson/.jshintrc

@@ -0,0 +1,34 @@
+{
+  "node": true,
+  "browser": true,
+  "esnext": true,
+  "bitwise": true,
+  "camelcase": true,
+  "curly": true,
+  "eqeqeq": true,
+  "immed": true,
+  "indent": 2,
+  "latedef": true,
+  "newcap": true,
+  "noarg": true,
+  "quotmark": "single",
+  "regexp": true,
+  "undef": true,
+  "unused": true,
+  "strict": true,
+  "trailing": true,
+  "smarttabs": false,
+  "maxlen": 80,
+  "maxdepth": 2,
+  "predef": [
+    "define",
+    "require",
+    "describe",
+    "it",
+    "xit",
+    "before",
+    "beforeEach",
+    "after",
+    "afterEach"
+  ]
+}

+ 3 - 0
test/hci-usecase/bundles/prettyjson/.npmignore

@@ -0,0 +1,3 @@
+.DS_Store
+node_modules/
+coverage/

+ 6 - 0
test/hci-usecase/bundles/prettyjson/.travis.yml

@@ -0,0 +1,6 @@
+language: "node_js"
+after_success: "npm run coveralls"
+node_js:
+  - "0.10"
+  - "0.12"
+  - "iojs"

+ 2 - 0
test/hci-usecase/bundles/prettyjson/Authors.md

@@ -0,0 +1,2 @@
+Go to the [https://github.com/rafeca/prettyjson/graphs/contributors](GitHub contributors graph page)
+to see the list of contributors.

+ 53 - 0
test/hci-usecase/bundles/prettyjson/CONTRIBUTING.md

@@ -0,0 +1,53 @@
+Good pull requests - patches, improvements, new features - are a fantastic
+help.
+
+If you've spotted any small, obvious errors and want to help out by patching it,
+that will be much appreciated.
+
+If your contribution involves a significant amount of work or substantial
+changes to any part of the project, please open a "contribution enquiry" issue
+first to check that the work is wanted or matches the goals of the project.
+
+All pull requests should remain focused in scope and avoid containing unrelated
+commits.
+
+Please follow this process; it's the best way to get your work included in the
+project:
+
+1. [Fork](http://help.github.com/fork-a-repo/) the project.
+
+2. Clone your fork (`git clone
+   git@github.com:<your-username>/<repo-name>.git`).
+
+3. Add an `upstream` remote (`git remote add upstream
+   git://github.com/<upsteam-owner>/<repo-name>.git`).
+
+4. Get the latest changes from upstream (e.g. `git pull upstream
+   <dev-branch>`).
+
+5. Create a new topic branch to contain your feature, change, or fix (`git
+   checkout -b <topic-branch-name>`).
+
+6. Create the needed tests to ensure that your contribution is not broken in the future.
+   If you are creating a small fix or patch to an existing feature, just a simple test
+   will do, if it is a brand new feature, make sure to create a new test suite.
+
+7. Make sure that your changes adhere to the current coding conventions used
+   throughout the project - indentation, accurate comments, etc.
+
+8. Commit your changes in logical chunks; use git's [interactive
+   rebase](https://help.github.com/articles/interactive-rebase) feature to tidy
+   up your commits before making them public. Please adhere to these [git commit
+   message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
+   or your pull request is unlikely be merged into the main project.
+
+9. Locally merge (or rebase) the upstream branch into your topic branch.
+
+10. Push your topic branch up to your fork (`git push origin
+   <topic-branch-name>`).
+
+11. [Open a Pull Request](http://help.github.com/send-pull-requests/) with a
+    clear title and description.
+
+If you have any other questions about contributing, please feel free to contact
+me.

+ 2 - 0
test/hci-usecase/bundles/prettyjson/History.md

@@ -0,0 +1,2 @@
+Go to [GitHub releases page](https://github.com/rafeca/prettyjson/releases) to
+see the history of releases.

+ 23 - 0
test/hci-usecase/bundles/prettyjson/LICENSE

@@ -0,0 +1,23 @@
+(The MIT License)
+
+Copyright (c) 2011 Rafael de Oleza <rafeca@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+

+ 134 - 0
test/hci-usecase/bundles/prettyjson/README.md

@@ -0,0 +1,134 @@
+# prettyjson [![Build Status](https://secure.travis-ci.org/rafeca/prettyjson.png)](http://travis-ci.org/rafeca/prettyjson) [![NPM version](https://badge.fury.io/js/prettyjson.png)](http://badge.fury.io/js/prettyjson) [![Coverage Status](https://coveralls.io/repos/rafeca/prettyjson/badge.png?branch=master)](https://coveralls.io/r/rafeca/prettyjson?branch=master)
+
+Package for formatting JSON data in a coloured YAML-style, perfect for CLI output.
+
+## How to install
+
+Just install it via NPM:
+
+```bash
+$ npm install -g prettyjson
+```
+
+This will install `prettyjson` globally, so it will be added automatically
+to your `PATH`.
+
+## Using it (from the CLI)
+
+This package installs a command line interface to render JSON data in a more
+convenient way. You can use the CLI in three different ways:
+
+**Decode a JSON file:** If you want to see the contents of a JSON file, just pass
+it as the first argument to the CLI:
+
+```bash
+$ prettyjson package.json
+```
+
+![Example 1](https://raw.github.com/rafeca/prettyjson/master/images/example3.png)
+
+**Decode the stdin:** You can also pipe the result of a command (for example an
+HTTP request) to the CLI to see the JSON result in a clearer way:
+
+```bash
+$ curl https://api.github.com/users/rafeca | prettyjson
+```
+
+![Example 2](https://raw.github.com/rafeca/prettyjson/master/images/example4.png)
+
+**Decode random strings:** if you call the CLI with no arguments, you'll get a
+prompt where you can past JSON strings and they'll be automatically displayed in a clearer way:
+
+![Example 3](https://raw.github.com/rafeca/prettyjson/master/images/example5.png)
+
+### Command line options
+
+It's possible to customize the output through some command line options:
+
+```bash
+# Change colors
+$ prettyjson --string=red --keys=blue --dash=yellow --number=green package.json
+
+# Do not use colors
+$ prettyjson --nocolor=1 package.json
+
+# Change indentation
+$ prettyjson --indent=4 package.json
+
+# Render arrays elements in a single line
+$ prettyjson --inline-arrays=1 package.json
+```
+
+**Deprecation Notice**: The old configuration through environment variables is
+deprecated and it will be removed in the next major version (1.0.0).
+
+## Using it (from Node.js)
+
+It's pretty easy to use it. You just have to include it in your script and call
+the `render()` method:
+
+```javascript
+var prettyjson = require('prettyjson');
+
+var data = {
+  username: 'rafeca',
+  url: 'https://github.com/rafeca',
+  twitter_account: 'https://twitter.com/rafeca',
+  projects: ['prettyprint', 'connfu']
+};
+
+var options = {
+  noColor: true
+};
+
+console.log(prettyjson.render(data, options));
+```
+
+And will output:
+
+![Example 4](https://raw.github.com/rafeca/prettyjson/master/images/example1.png)
+
+You can also configure the colors of the hash keys and array dashes
+(using [colors.js](https://github.com/Marak/colors.js) colors syntax):
+
+```javascript
+var prettyjson = require('prettyjson');
+
+var data = {
+  username: 'rafeca',
+  url: 'https://github.com/rafeca',
+  twitter_account: 'https://twitter.com/rafeca',
+  projects: ['prettyprint', 'connfu']
+};
+
+console.log(prettyjson.render(data, {
+  keysColor: 'rainbow',
+  dashColor: 'magenta',
+  stringColor: 'white'
+}));
+```
+
+Will output something like:
+
+![Example 5](https://raw.github.com/rafeca/prettyjson/master/images/example2.png)
+
+## Running Tests
+
+To run the test suite first invoke the following command within the repo,
+installing the development dependencies:
+
+```bash
+$ npm install
+```
+
+then run the tests:
+
+```bash
+$ npm test
+```
+
+On windows, you can run the tests with:
+
+```cmd
+C:\git\prettyjson> npm run-script testwin
+```

+ 48 - 0
test/hci-usecase/bundles/prettyjson/bin/prettyjson

@@ -0,0 +1,48 @@
+#!/usr/bin/env node
+
+var prettyjson = require('../lib/prettyjson');
+var fs=require('fs');
+var argv = require('minimist')(process.argv.slice(2));
+
+var options = {
+  keysColor: argv.keys || process.env.PRETTYJSON_KEYS,
+  dashColor: argv.dash || process.env.PRETTYJSON_DASH,
+  defaultIndentation: argv.indent || process.env.PRETTYJSON_INDENT,
+  stringColor: argv.string || process.env.PRETTYJSON_STRING,
+  numberColor: argv.number || process.env.PRETTYJSON_NUMBER,
+  noColor: argv['nocolor'] || process.env.PRETTYJSON_NOCOLOR,
+  inlineArrays: argv['inline-arrays'] || process.env.PRETTYJSON_INLINE_ARRAYS
+};
+
+var renderInputJson = function(input){
+  console.log(prettyjson.renderString(input, options));
+};
+
+if (argv._.length) {
+  // First parameter is the file to read and parse
+  var filename = argv._[0];
+  try {
+    renderInputJson(fs.readFileSync(filename, 'utf8'));
+  } catch (e) {
+    console.error('Error: '.red + "File '" + filename + "' does not exist");
+    process.exit(1);
+  }
+} else {
+  // Read input stream
+
+  var streamData = '';
+
+  process.stdin.resume();
+  process.stdin.setEncoding('utf8');
+  process.stdin.on('data', function (chunk) {
+    if (chunk === '\n') {
+      renderInputJson(streamData);
+      streamData = '';
+      return;
+    }
+    streamData += chunk;
+  });
+  process.stdin.on('end', function(){
+    renderInputJson(streamData);
+  });
+}

TEMPAT SAMPAH
test/hci-usecase/bundles/prettyjson/images/example1.png


TEMPAT SAMPAH
test/hci-usecase/bundles/prettyjson/images/example2.png


TEMPAT SAMPAH
test/hci-usecase/bundles/prettyjson/images/example3.png


TEMPAT SAMPAH
test/hci-usecase/bundles/prettyjson/images/example4.png


TEMPAT SAMPAH
test/hci-usecase/bundles/prettyjson/images/example5.png


+ 229 - 0
test/hci-usecase/bundles/prettyjson/lib/prettyjson.js

@@ -0,0 +1,229 @@
+'use strict';
+
+// ### Module dependencies
+require('colors');
+var Utils = require('./utils');
+
+exports.version = require('../package.json').version;
+
+// ### Render function
+// *Parameters:*
+//
+// * **`data`**: Data to render
+// * **`options`**: Hash with different options to configure the parser
+// * **`indentation`**: Base indentation of the parsed output
+//
+// *Example of options hash:*
+//
+//     {
+//       emptyArrayMsg: '(empty)', // Rendered message on empty strings
+//       keysColor: 'blue',        // Color for keys in hashes
+//       dashColor: 'red',         // Color for the dashes in arrays
+//       stringColor: 'grey',      // Color for strings
+//       defaultIndentation: 2     // Indentation on nested objects
+//     }
+exports.render = function render(data, options, indentation) {
+  // Default values
+  indentation = indentation || 0;
+  options = options || {};
+  options.emptyArrayMsg = options.emptyArrayMsg || '(empty array)';
+  options.keysColor = options.keysColor || 'green';
+  options.dashColor = options.dashColor || 'green';
+  options.numberColor = options.numberColor || 'blue';
+  options.defaultIndentation = options.defaultIndentation || 2;
+  options.noColor = !!options.noColor;
+
+  options.stringColor = options.stringColor || null;
+
+  var output = [];
+
+  // Helper function to detect if an object can be directly serializable
+  var isSerializable = function(input, onlyPrimitives) {
+    if (typeof input === 'boolean' ||
+        typeof input === 'number' || input === null ||
+		input instanceof Date) {
+      return true;
+    }
+    if (typeof input === 'string' && input.indexOf('\n') === -1) {
+      return true;
+    }
+
+    if (options.inlineArrays && !onlyPrimitives) {
+      if (Array.isArray(input) && isSerializable(input[0], true)) {
+        return true;
+      }
+    }
+
+    return false;
+  };
+
+  var indentLines = function(string, spaces){
+    var lines = string.split('\n');
+    lines = lines.map(function(line){
+      return Utils.indent(spaces) + line;
+    });
+    return lines.join('\n');
+  };
+
+  var addColorToData = function(input) {
+    if (options.noColor) {
+      return input;
+    }
+
+    if (typeof input === 'string') {
+      // Print strings in regular terminal color
+      return options.stringColor ? input[options.stringColor] : input;
+    }
+
+    var sInput = input + '';
+
+    if (input === true) {
+      return sInput.green;
+    }
+    if (input === false) {
+      return sInput.red;
+    }
+    if (input === null) {
+      return sInput.grey;
+    }
+    if (typeof input === 'number') {
+      return sInput[options.numberColor];
+    }
+    if (Array.isArray(input)) {
+      return input.join(', ');
+    }
+
+    return sInput;
+  };
+
+  // Render a string exactly equal
+  if (isSerializable(data)) {
+    output.push(Utils.indent(indentation) + addColorToData(data));
+  }
+  else if (typeof data === 'string') {
+    //unserializable string means it's multiline
+    output.push(Utils.indent(indentation) + '"""');
+    output.push(indentLines(data, indentation + options.defaultIndentation));
+    output.push(Utils.indent(indentation) + '"""');
+  }
+  else if (Array.isArray(data)) {
+    // If the array is empty, render the `emptyArrayMsg`
+    if (data.length === 0) {
+      output.push(Utils.indent(indentation) + options.emptyArrayMsg);
+    } else {
+      data.forEach(function(element) {
+        // Prepend the dash at the begining of each array's element line
+        var line = ('- ');
+        if (!options.noColor) {
+          line = line[options.dashColor];
+        }
+        line = Utils.indent(indentation) + line;
+
+        // If the element of the array is a string, bool, number, or null
+        // render it in the same line
+        if (isSerializable(element)) {
+          line += exports.render(element, options);
+          output.push(line);
+
+        // If the element is an array or object, render it in next line
+        } else {
+          output.push(line);
+          output.push(exports.render(
+            element, options, indentation + options.defaultIndentation
+          ));
+        }
+      });
+    }
+  }
+  else if (typeof data === 'object') {
+    // Get the size of the longest index to align all the values
+    var maxIndexLength = Utils.getMaxIndexLength(data);
+    var key;
+    var isError = data instanceof Error;
+
+    Object.getOwnPropertyNames(data).forEach(function(i) {
+      // Prepend the index at the beginning of the line
+      key = (i + ': ');
+      if (!options.noColor) {
+        key = key[options.keysColor];
+      }
+      key = Utils.indent(indentation) + key;
+
+      // Skip `undefined`, it's not a valid JSON value.
+      if (data[i] === undefined) {
+        return;
+      }
+
+      // If the value is serializable, render it in the same line
+      if (isSerializable(data[i]) && (!isError || i !== 'stack')) {
+        key += exports.render(data[i], options, maxIndexLength - i.length);
+        output.push(key);
+
+        // If the index is an array or object, render it in next line
+      } else {
+        output.push(key);
+        output.push(
+          exports.render(
+            isError && i === 'stack' ? data[i].split('\n') : data[i],
+            options,
+            indentation + options.defaultIndentation
+          )
+        );
+      }
+    });
+  }
+  // Return all the lines as a string
+  return output.join('\n');
+};
+
+// ### Render from string function
+// *Parameters:*
+//
+// * **`data`**: Data to render as a string
+// * **`options`**: Hash with different options to configure the parser
+// * **`indentation`**: Base indentation of the parsed output
+//
+// *Example of options hash:*
+//
+//     {
+//       emptyArrayMsg: '(empty)', // Rendered message on empty strings
+//       keysColor: 'blue',        // Color for keys in hashes
+//       dashColor: 'red',         // Color for the dashes in arrays
+//       defaultIndentation: 2     // Indentation on nested objects
+//     }
+exports.renderString = function renderString(data, options, indentation) {
+
+  var output = '';
+  var parsedData;
+  // If the input is not a string or if it's empty, just return an empty string
+  if (typeof data !== 'string' || data === '') {
+    return '';
+  }
+
+  // Remove non-JSON characters from the beginning string
+  if (data[0] !== '{' && data[0] !== '[') {
+    var beginingOfJson;
+    if (data.indexOf('{') === -1) {
+      beginingOfJson = data.indexOf('[');
+    } else if (data.indexOf('[') === -1) {
+      beginingOfJson = data.indexOf('{');
+    } else if (data.indexOf('{') < data.indexOf('[')) {
+      beginingOfJson = data.indexOf('{');
+    } else {
+      beginingOfJson = data.indexOf('[');
+    }
+    output += data.substr(0, beginingOfJson) + '\n';
+    data = data.substr(beginingOfJson);
+  }
+
+  try {
+    parsedData = JSON.parse(data);
+  } catch (e) {
+    // Return an error in case of an invalid JSON
+    return 'Error:'.red + ' Not valid JSON!';
+  }
+
+  // Call the real render() method
+  output += exports.render(parsedData, options, indentation);
+  return output;
+};

+ 25 - 0
test/hci-usecase/bundles/prettyjson/lib/utils.js

@@ -0,0 +1,25 @@
+'use strict';
+
+/**
+ * Creates a string with the same length as `numSpaces` parameter
+ **/
+exports.indent = function indent(numSpaces) {
+  return new Array(numSpaces+1).join(' ');
+};
+
+/**
+ * Gets the string length of the longer index in a hash
+ **/
+exports.getMaxIndexLength = function(input) {
+  var maxWidth = 0;
+
+  Object.getOwnPropertyNames(input).forEach(function(key) {
+    // Skip undefined values.
+    if (input[key] === undefined) {
+      return;
+    }
+
+    maxWidth = Math.max(maxWidth, key.length);
+  });
+  return maxWidth;
+};

+ 105 - 0
test/hci-usecase/bundles/prettyjson/package.json

@@ -0,0 +1,105 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "prettyjson",
+        "scope": null,
+        "escapedName": "prettyjson",
+        "name": "prettyjson",
+        "rawSpec": "",
+        "spec": "latest",
+        "type": "tag"
+      },
+      "/home/stdio/coding/research/jspak-dev/jspak/bundles"
+    ]
+  ],
+  "_from": "prettyjson@latest",
+  "_id": "prettyjson@1.1.3",
+  "_inCache": true,
+  "_location": "/prettyjson",
+  "_nodeVersion": "0.12.7",
+  "_npmUser": {
+    "name": "rafeca",
+    "email": "rafeca@gmail.com"
+  },
+  "_npmVersion": "2.11.3",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "prettyjson",
+    "scope": null,
+    "escapedName": "prettyjson",
+    "name": "prettyjson",
+    "rawSpec": "",
+    "spec": "latest",
+    "type": "tag"
+  },
+  "_requiredBy": [
+    "#USER"
+  ],
+  "_resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.1.3.tgz",
+  "_shasum": "d0787f732c9c3a566f4165fa4f1176fd67e6b263",
+  "_shrinkwrap": null,
+  "_spec": "prettyjson",
+  "_where": "/home/stdio/coding/research/jspak-dev/jspak/bundles",
+  "author": {
+    "name": "Rafael de Oleza",
+    "email": "rafeca@gmail.com",
+    "url": "https://github.com/rafeca"
+  },
+  "bin": {
+    "prettyjson": "./bin/prettyjson"
+  },
+  "bugs": {
+    "url": "https://github.com/rafeca/prettyjson/issues"
+  },
+  "dependencies": {
+    "colors": "^1.1.2",
+    "minimist": "^1.1.3"
+  },
+  "description": "Package for formatting JSON data in a coloured YAML-style, perfect for CLI output",
+  "devDependencies": {
+    "coveralls": "^2.11.3",
+    "istanbul": "^0.3.17",
+    "jshint": "^2.4.4",
+    "mocha": "^2.2.5",
+    "mocha-lcov-reporter": "0.0.2",
+    "should": "^7.0.2"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "d0787f732c9c3a566f4165fa4f1176fd67e6b263",
+    "tarball": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.1.3.tgz"
+  },
+  "gitHead": "99dac22baba027875536f543f8dbe5b4f9d9f986",
+  "homepage": "http://rafeca.com/prettyjson",
+  "keywords": [
+    "json",
+    "cli",
+    "formatting",
+    "colors"
+  ],
+  "license": "MIT",
+  "main": "./lib/prettyjson",
+  "maintainers": [
+    {
+      "name": "rafeca",
+      "email": "rafeca@gmail.com"
+    }
+  ],
+  "name": "prettyjson",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/rafeca/prettyjson.git"
+  },
+  "scripts": {
+    "changelog": "git log $(git describe --tags --abbrev=0)..HEAD --pretty='* %s' --first-parent",
+    "coverage": "istanbul cover _mocha --report lcovonly -- -R spec",
+    "coveralls": "npm run coverage && cat ./coverage/lcov.info | coveralls && rm -rf ./coverage",
+    "jshint": "jshint lib/*.js",
+    "test": "npm run jshint && mocha --reporter spec",
+    "testwin": "node ./node_modules/mocha/bin/mocha --reporter spec"
+  },
+  "version": "1.1.3"
+}

+ 348 - 0
test/hci-usecase/bundles/prettyjson/test/prettyjson_spec.js

@@ -0,0 +1,348 @@
+var prettyjson = process.env.EXPRESS_COV ? require('../lib-cov/prettyjson') : require('../lib/prettyjson');
+var should = require('should');
+
+describe('prettyjson general tests', function() {
+
+  it("should output a string exactly equal as the input", function() {
+
+    var input = 'This is a string';
+    var output = prettyjson.render(input);
+
+    output.should.equal(input);
+  });
+
+  it("should output a string with indentation", function() {
+
+    var input = 'This is a string';
+    var output = prettyjson.render(input, {}, 4);
+
+    output.should.equal('    ' + input);
+  });
+
+  it("should output a multiline string with indentation", function() {
+
+    var input = 'multiple\nlines'
+    var output = prettyjson.render(input, {}, 4);
+
+    output.should.equal('    """\n      multiple\n      lines\n    """');
+  });
+
+  it("should output an array of strings", function() {
+
+    var input = ['first string', 'second string'];
+    var output = prettyjson.render(input);
+
+    output.should.equal([
+      '- '.green + input[0],
+      '- '.green + input[1]
+    ].join('\n'));
+  });
+
+  it("should output an array of arrays", function() {
+
+    var input = ['first string', ['nested 1', 'nested 2'], 'second string'];
+    var output = prettyjson.render(input);
+
+    output.should.equal([
+      '- '.green + input[0],
+      '- '.green,
+      '  ' + '- '.green + input[1][0],
+      '  ' + '- '.green + input[1][1],
+      '- '.green + input[2]
+    ].join('\n'));
+  });
+
+  it("should output a hash of strings", function() {
+
+    var input = {param1: 'first string', param2: 'second string'};
+    var output = prettyjson.render(input);
+
+    output.should.equal([
+      'param1: '.green + 'first string',
+      'param2: '.green + 'second string'
+    ].join('\n'));
+  });
+
+  it("should output a hash of hashes", function() {
+
+    var input = {first_param: {subparam: 'first string', subparam2: 'another string'}, second_param: 'second string'};
+    var output = prettyjson.render(input);
+
+    output.should.equal([
+      'first_param: '.green,
+      '  ' + 'subparam: '.green + ' first string',
+      '  ' + 'subparam2: '.green + 'another string',
+      'second_param: '.green + 'second string'
+    ].join('\n'));
+  });
+
+  it("should indent correctly the hashes keys", function() {
+
+    var input = {very_large_param: 'first string', param: 'second string'};
+    var output = prettyjson.render(input);
+
+    output.should.equal([
+      'very_large_param: '.green + 'first string',
+      'param: '.green + '           second string'
+    ].join('\n'));
+  });
+
+  it("should output a really nested object", function() {
+
+    var input = {
+      first_param: {
+        subparam: 'first string',
+        subparam2: 'another string',
+        subparam3: ["different", "values", "in an array"]
+      },
+      second_param: 'second string',
+      an_array: [{
+        param3: 'value',
+        param10: 'other value'
+      }],
+      empty_array: []
+    };
+
+    var output = prettyjson.render(input);
+
+    output.should.equal([
+      'first_param: '.green,
+      '  ' + 'subparam: '.green + ' first string',
+      '  ' + 'subparam2: '.green + 'another string',
+      '  ' + 'subparam3: '.green,
+      '    ' + '- '.green + 'different',
+      '    ' + '- '.green + 'values',
+      '    ' + '- '.green + 'in an array',
+      'second_param: '.green + 'second string',
+      'an_array: '.green,
+      '  ' + '- '.green,
+      '    ' + 'param3: '.green + ' value',
+      '    ' + 'param10: '.green + 'other value',
+      'empty_array: '.green,
+      '  (empty array)'
+    ].join('\n'));
+  });
+
+  it("should allow to configure colors for hash keys", function() {
+    var input = {param1: 'first string', param2: 'second string'};
+    var output = prettyjson.render(input, {keysColor: 'blue'});
+
+    output.should.equal([
+      'param1: '.blue + 'first string',
+      'param2: '.blue + 'second string'
+    ].join('\n'));
+  });
+
+  it("should allow to configure colors for numbers", function() {
+    var input = {param1: 17, param2: 22.3};
+    var output = prettyjson.render(input, {numberColor: 'red'});
+
+    output.should.equal([
+      'param1: '.green + '17'.red,
+      'param2: '.green + '22.3'.red
+    ].join('\n'));
+  });
+
+  it("should allow to configure rainbow as color", function() {
+    var input = {param_long: 'first string', param2: 'second string'};
+    var output = prettyjson.render(input, {keysColor: 'rainbow'});
+
+    output.should.equal([
+      'param_long: '.rainbow + 'first string',
+      'param2: '.rainbow + '    second string'
+    ].join('\n'));
+  });
+
+  it("should allow to configure the default indentation", function() {
+    var input = {param: ['first string', "second string"]};
+    var output = prettyjson.render(input, {defaultIndentation: 4});
+
+    output.should.equal([
+      'param: '.green,
+      '    ' + '- '.green + 'first string',
+      '    ' + '- '.green + 'second string'
+    ].join('\n'));
+  });
+
+  it("should allow to configure the empty message for arrays", function() {
+    var input = [];
+    var output = prettyjson.render(input, {emptyArrayMsg: '(empty)'});
+
+    output.should.equal([
+      '(empty)'
+    ].join('\n'));
+  });
+
+  it("should allow to configure colors for strings", function() {
+    var input = {param1: 'first string', param2: 'second string'};
+    var output = prettyjson.render(input, {keysColor: 'blue', stringColor: 'red'});
+
+    output.should.equal([
+      'param1: '.blue + 'first string'.red,
+      'param2: '.blue + 'second string'.red
+    ].join('\n'));
+  });
+
+  it("should allow to not use colors", function() {
+    var input = {param1: 'first string', param2: ['second string']};
+    var output = prettyjson.render(input, {noColor: true});
+
+    output.should.equal([
+      'param1: first string',
+      'param2: ',
+      '  - second string'
+    ].join('\n'));
+  });
+
+  it("should allow to print simple arrays inline", function() {
+    var input = {installs: ['first string', 'second string', false, 13]};
+    var output = prettyjson.render(input, {inlineArrays: true});
+
+    output.should.equal(
+      'installs: '.green + 'first string, second string, false, 13');
+
+    input = {installs: [ ['first string', 'second string'], 'third string']};
+    output = prettyjson.render(input, {inlineArrays: true});
+
+    output.should.equal([
+      'installs: '.green,
+      '  ' + '- '.green + 'first string, second string',
+      '  ' + '- '.green + 'third string'
+      ].join('\n'));
+  });
+
+  it("should not print an object prototype", function() {
+    var Input = function() {
+      this.param1 = 'first string';
+      this.param2 = 'second string';
+    };
+    Input.prototype = {randomProperty: 'idontcare'};
+
+    var output = prettyjson.render(new Input);
+
+    output.should.equal([
+      'param1: '.green + 'first string',
+      'param2: '.green + 'second string'
+    ].join('\n'));
+  });
+});
+
+describe('Printing numbers, booleans and other objects', function() {
+  it("should print numbers correctly ", function() {
+    var input = 12345;
+    var output = prettyjson.render(input, {}, 4);
+
+    output.should.equal('    ' + '12345'.blue);
+  });
+
+  it("should print booleans correctly ", function() {
+    var input = true;
+    var output = prettyjson.render(input, {}, 4);
+
+    output.should.equal('    ' + 'true'.green);
+
+    input = false;
+    output = prettyjson.render(input, {}, 4);
+
+    output.should.equal('    ' + 'false'.red);
+  });
+
+  it("should print a null object correctly ", function() {
+    var input = null;
+    var output = prettyjson.render(input, {}, 4);
+
+    output.should.equal('    ' + 'null'.grey);
+  });
+
+  it("should print an Error correctly ", function() {
+    Error.stackTraceLimit = 1;
+    var input = new Error('foo');
+    var stack = input.stack.split('\n');
+    var output = prettyjson.render(input, {}, 4);
+
+    output.should.equal([
+      '    ' + 'stack: '.green,
+      '      ' + '- '.green + stack[0],
+      '      ' + '- '.green + stack[1],
+      '    ' + 'message: '.green + 'foo'
+    ].join('\n'));
+  });
+
+  it('should print serializable items in an array inline', function() {
+	var dt = new Date();
+    var output = prettyjson.render([ 'a', 3, null, true, false, dt]);
+
+    output.should.equal([
+      '- '.green + 'a',
+      '- '.green + '3'.blue,
+      '- '.green + 'null'.grey,
+      '- '.green + 'true'.green,
+      '- '.green + 'false'.red,
+	  '- '.green + dt
+    ].join('\n'));
+  });
+
+  it('should print dates correctly', function() {
+	var input = new Date();
+	var expected = input.toString();
+	var output = prettyjson.render(input, {}, 4);
+
+	output.should.equal('    ' + expected);
+  });
+
+  it('should print dates in objects correctly', function() {
+	var dt1 = new Date();
+	var dt2 = new Date();
+
+	var input = {
+		dt1: dt2,
+		dt2: dt2
+	};
+
+	var output = prettyjson.render(input, {}, 4);
+
+	output.should.equal([
+		'    ' + 'dt1: '.green + dt1.toString(),
+		'    ' + 'dt2: '.green + dt2.toString()].join('\n'));
+  });
+});
+
+describe('prettyjson.renderString() method', function(){
+  it('should return an empty string if input is empty', function(){
+    var input = '';
+
+    var output = prettyjson.renderString(input);
+
+    output.should.equal('');
+  });
+
+  it('should return an empty string if input is not a string', function(){
+    var output = prettyjson.renderString({});
+    output.should.equal('');
+  });
+
+  it('should return an error message if the input is an invalid JSON string', function(){
+    var output = prettyjson.renderString('not valid!!');
+    output.should.equal('Error:'.red + ' Not valid JSON!');
+  });
+
+  it('should return the prettyfied string if it is a valid JSON string', function(){
+    var output = prettyjson.renderString('{"test": "OK"}');
+    output.should.equal('test: '.green + 'OK');
+  });
+
+  it('should dismiss trailing characters which are not JSON', function(){
+    var output = prettyjson.renderString('characters that are not JSON at all... {"test": "OK"}');
+    output.should.equal("characters that are not JSON at all... \n" + 'test: '.green + 'OK');
+  });
+
+  it('should dismiss trailing characters which are not JSON with an array', function(){
+    var output = prettyjson.renderString('characters that are not JSON at all... ["test"]');
+    output.should.equal("characters that are not JSON at all... \n" + '- '.green + 'test');
+  });
+
+  it('should be able to accept the options parameter', function(){
+    var output = prettyjson.renderString('{"test": "OK"}', {stringColor: 'red'});
+    output.should.equal('test: '.green + 'OK'.red);
+  });
+});

+ 24 - 0
test/hci-usecase/bundles/shell-test/index.js

@@ -0,0 +1,24 @@
+var log = require('hlogger').createLogger('shell-test');
+var child = require('child_process');
+
+function bundleStart(context) {
+
+	log.info("Command provider installed");
+
+	// Create a bash and transport commands
+	context.after('.*:cmd:.*',(args,e) => {
+		if(e.count >0) return;
+
+		e.wait();	
+		var proc = child.exec(args.join(" "));
+		proc.stdout.pipe(process.stdout);
+		proc.stderr.pipe(process.stderr);
+
+		proc.on('close',function() {
+			e.done();
+		});			
+		e.done();
+	});
+}
+
+module.exports.bundleStart = bundleStart;

+ 12 - 0
test/hci-usecase/package.json

@@ -0,0 +1,12 @@
+{
+  "name": "@hexasoftware/hci-usecase",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "ISC"
+}