├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── README_zh.md ├── assets ├── dist │ ├── build.js │ └── build.js.map ├── index.html ├── memeye-logo.png └── memeye.ico ├── client ├── .babelrc ├── .gitignore ├── README.md ├── index.html ├── package.json ├── src │ ├── App.vue │ ├── assets │ │ ├── logo.png │ │ └── memeye.ico │ ├── components │ │ ├── Memeye.vue │ │ ├── Panel.vue │ │ ├── PanelItem.vue │ │ └── charts │ │ │ ├── OSCPUSUsedLine.js │ │ │ ├── OSMemoryLine.js │ │ │ ├── OSMemoryPie.js │ │ │ ├── ProcessDoughnut.js │ │ │ ├── ProcessLine.js │ │ │ ├── V8HeapSpaceBar.js │ │ │ └── V8HeapSpacePie.js │ └── main.js ├── webpack.config.js └── yarn.lock ├── coverage ├── coverage.json ├── lcov-report │ ├── base.css │ ├── index.html │ ├── lib │ │ └── index.html │ ├── prettify.css │ ├── prettify.js │ ├── sort-arrow-sprite.png │ └── sorter.js └── lcov.info ├── example └── small-example.js ├── index.js ├── package.json ├── src ├── dashboard │ ├── index.js │ └── server.js ├── index.js └── lib │ ├── Collector.js │ ├── Indicators.js │ └── Logger.js ├── test ├── bootstarp.js └── unit │ ├── Collector.test.js │ └── Indicators.test.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | coverage 4 | npm-debug.log 5 | yarn-error.log -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .gitignore 3 | .travis.yml 4 | client 5 | coverage 6 | example 7 | test 8 | node_modules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | - "6" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2017 JC-Huang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | memeye logo 4 |
5 | 6 | [![Build Status](https://travis-ci.org/JerryC8080/Memeye.svg?branch=master)](https://travis-ci.org/JerryC8080/Memeye) 7 | [![npm version](https://badge.fury.io/js/memeye.svg)](https://badge.fury.io/js/memeye) 8 | [![Package Quality](http://npm.packagequality.com/shield/memeye.svg)](http://packagequality.com/#?package=memeye) 9 | [![npm](https://img.shields.io/npm/dt/memeye.svg)](https://www.npmjs.com/package/memeye) 10 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/JerryC8080/Memeye/blob/master/LICENSE) 11 | 12 | # Memeye 13 | [中文版](README_zh.md) 14 | 15 | # Introduction 16 | 17 | Memeye is a lightweight NodeJS process monitoring tool that provides data visualization of process memory, V8 heap space memory, and operating system memory. 18 | The front part, with [Vue2](https://github.com/vuejs/vue) and [ChartJS](https://github.com/chartjs/Chart.js), provides a nice dynamic dashboard. 19 | Memeye in the host process, only the implantation of a simple data collector, the other work is to start a child-process, by the child-process to carry out. 20 | This will minimize the impact of Memeye's code on the host process to ensure the authenticity of the data. 21 | 22 | ### Feature 23 | - Lightweight 24 | - Simple 25 | - For development 26 | - Visualization 27 | 28 | 29 | *Note: Memeye only support a single process, NodeJS distributed process is not applicable, it is not recommended in the product environment。* 30 | 31 | # Motivation 32 | As we all know, NodeJS is very sensitive to memory. 33 | In April last year I used NodeJS to build a marketing project, on the day of project online PV broke one million. 34 | Which is showing a continuous upward trend in memory, in the process of troubleshooting problems, i want to find a lightweight, as long as the visual display of the use of memory tools, but no result. 35 | Then there is the idea of this project, but at that time because of the busy only made a simple Demo to use (Memeye v0.0.3). 36 | Recently there is time, once again turned out to reorganize and revision, add more type of the data display. 37 | 38 | 39 | # Demo 40 | [See preview demo](http://jerryc8080.github.io/Memeye/) 41 | 42 | # Compatibility 43 | - Node v7.x 44 | - Node v6.x 45 | 46 | # Install & Usage 47 | 48 | For install simply run : 49 | 50 | ``` 51 | npm install memeye --save-dev 52 | ``` 53 | 54 | Then require it in your nodejs application 55 | 56 | ``` 57 | const memeye = require('memeye'); 58 | memeye(); 59 | ``` 60 | 61 | Then open your browser and load address: 62 | 63 | ``` 64 | http://localhost:23333 //23333 port by default. 65 | ``` 66 | 67 | That's it! No more options, no more config, just so easy. 68 | 69 | # How it works 70 | 71 | Memeye has three core concept: Collector, Indicator and Dashboard. 72 | While Collector runing in your nodejs process, Indicators and Dashboard runing on the child-process, in this way Memeye will make as little influence as possible to your nodejs process. 73 | 74 | ## Collector 75 | Collector will wathching and collecting data from the host node process、v8 heap and operrating system ,then send the datas to dashboard process with IPC communication channel. 76 | 77 | ## Indicator 78 | Indicator like a state machine . When attribute changed, the instance of Indicator will emit an event. So we can use it to handle our indicators data of process, v8 heap and OS. 79 | 80 | ## Dashboard 81 | 82 | The dashboard , will calling at the child process way. It will create an Indicator instance and start a http server which provide a socket.io instance. 83 | Then bind the indicator with process IPC channel, to recive massage from parent process. 84 | And then bind the indicator with socket.io, to send messages while indicator attrbutes changed. 85 | 86 | ## The commication between Collector, Indicator and Dashboard 87 | 88 | ![commication.jpeg](https://raw.githubusercontent.com/JerryC8080/figure-bed/master/img/20190717214117.jpg) 89 | 90 | # Test 91 | Simply run: 92 | 93 | ``` 94 | npm test 95 | ``` 96 | 97 | # Some feature may be 98 | - [ ] Mutil process support 99 | - [ ] Report export 100 | 101 | # License 102 | 103 | [MIT License](LICENSE) 104 | 105 | Copyright (c) 2016-2017 JerryC 106 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 |
2 | memeye logo 3 |
4 | 5 | [![Build Status](https://travis-ci.org/JerryC8080/Memeye.svg?branch=master)](https://travis-ci.org/JerryC8080/Memeye) 6 | [![npm version](https://badge.fury.io/js/memeye.svg)](https://badge.fury.io/js/memeye) 7 | [![Package Quality](http://npm.packagequality.com/shield/memeye.svg)](http://packagequality.com/#?package=memeye) 8 | [![npm](https://img.shields.io/npm/dt/memeye.svg)](https://www.npmjs.com/package/memeye) 9 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/JerryC8080/Memeye/blob/master/LICENSE) 10 | 11 | # Memeye 12 | [中文版](README_zh.md) 13 | 14 | # 介绍 15 | Memeye 是一个轻量级的 NodeJS 进程监控工具,它提供 进程内存、V8 堆空间内存、操作系统内存 三大维度的数据可视化展示。 16 | 前端部分,借助 [Vue2](https://github.com/vuejs/vue) 和 [ChartJS](https://github.com/chartjs/Chart.js) 提供了一个不错的动态展示面板。 17 | Memeye 在宿主进程中,只植入了一个简单的数据收集器,其他工作则启动一个子进程,交由子进程来进行。 18 | 这样做能把 Memeye 的代码对宿主进程的影响降到最低,以确保数据的真实性。 19 | 20 | ### 特点 21 | - 轻量级 22 | - 简单 23 | - 面向开发环境 24 | - 可视化 25 | 26 | *Note: Memeye 暂时只支持单进程,NodeJS 分布式进程还不适用,所以不建议在产品环境使用。* 27 | 28 | # 动机 29 | 总所周知,NodeJS 对内存是很敏感的。在去年 4 月我用 NodeJS 做的一个营销性的项目,在上线当天 PV 突破了 100W。 30 | 其中内存就呈现出持续上涨趋势,在排查问题的过程中,想寻找一个轻量级的,只要可视化的呈现内存使用情况的工具,无果。 31 | 然后就有了这个项目的想法,但当时由于繁忙只做出了一个 Demo 级别的,简单能用就发布了。 32 | 最近有时间,再次翻出来,重构改版,增加更多维度的数据展示。 33 | 34 | # Demo 35 | [See preview demo](http://jerryc8080.github.io/Memeye/) (也许需要翻墙) 36 | 37 | # 兼容性 38 | - Node v7.x 39 | - Node v6.x 40 | 41 | # 安装 & 使用 42 | 43 | 运行下面命令安装 : 44 | 45 | ``` 46 | npm install memeye --save-dev 47 | ``` 48 | 49 | 50 | 然后在你的代码中引入 51 | 52 | ``` 53 | const memeye = require('memeye'); 54 | memeye(); 55 | ``` 56 | 57 | 最后打开你的浏览器,输入下面地址: 58 | 59 | ``` 60 | http://localhost:23333 //23333 port by defaul. 61 | ``` 62 | 63 | 就这么简单! 64 | 65 | # Memeye 是如何工作的 66 | 67 | Memeye 有三个核心概念:Collector, Indicators, Dashboard。 68 | Collector 运行在宿主进程中(你的NodeJS进程),Indicator 和 Dashboard 运行在子进程中,这样可以尽量减少 Memeye 代码对你的宿主进程的影响。 69 | 70 | ## Collector 71 | Collector 会监听宿主进程,并且收集数据,然后通过 IPC 通信管道发送数据给子进程,交由子进程处理。 72 | 73 | ## Indicator 74 | Indicator 像一个状态机。当它的属性变化的时候,会触发相应事件。所以我们可以用它来处理收集回来的数据。 75 | 76 | ## Dashboard 77 | Dashboard 会以子进程的形式唤起。他会创建一个 Indicator 实例,以及启动一个集成 socket.io 的 Http 服务器。 78 | 然后绑定 Indicator 和进程通信管道,以接收父进程发过来的数据。 79 | 最后再绑定 Indicator 和 socket.io,这样可以在 Indicator 属性变化的时候发送数据给前端。 80 | 81 | ## Collector, Indicator, Dashboard 之间的通信 82 | 83 | ![commication.jpeg](https://raw.githubusercontent.com/JerryC8080/figure-bed/master/img/20190717214117.jpg) 84 | 85 | 86 | # 测试 87 | 88 | ``` 89 | npm test 90 | ``` 91 | 92 | # 在考虑的功能 93 | - [ ] 支持多进程 94 | - [ ] 支持导出报告 95 | 96 | # 许可 97 | 98 | [MIT License](LICENSE) 99 | 100 | Copyright (c) 2016-2017 JerryC 101 | -------------------------------------------------------------------------------- /assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | memeye-client 8 | 9 | 10 | 11 |
12 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /assets/memeye-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JerryC8080/Memeye/3ebd694b892896221b9eba28c5730c5d27be978b/assets/memeye-logo.png -------------------------------------------------------------------------------- /assets/memeye.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JerryC8080/Memeye/3ebd694b892896221b9eba28c5730c5d27be978b/assets/memeye.ico -------------------------------------------------------------------------------- /client/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["latest", { 4 | "es2015": { "modules": false } 5 | }] 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | yarn-error.log 6 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # memeye-client 2 | 3 | > A Vue.js project 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # build for production with minification 15 | npm run build 16 | ``` 17 | 18 | For detailed explanation on how things work, consult the [docs for vue-loader](http://vuejs.github.io/vue-loader). 19 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | memeye-client 8 | 9 | 10 | 11 |
12 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "memeye-client", 3 | "description": "A Vue.js project", 4 | "version": "1.0.0", 5 | "author": "JerryC", 6 | "private": true, 7 | "scripts": { 8 | "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot", 9 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" 10 | }, 11 | "dependencies": { 12 | "socket.io-client": "^1.7.3", 13 | "vue": "^2.2.1", 14 | "vue-chartjs": "^2.4.1" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.0.0", 18 | "babel-loader": "^6.0.0", 19 | "babel-preset-latest": "^6.0.0", 20 | "cross-env": "^3.0.0", 21 | "css-loader": "^0.25.0", 22 | "file-loader": "^0.9.0", 23 | "vue-loader": "^11.0.0", 24 | "vue-template-compiler": "^2.2.1", 25 | "webpack": "^2.2.0", 26 | "webpack-dev-server": "^2.2.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /client/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 23 | 24 | 51 | -------------------------------------------------------------------------------- /client/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JerryC8080/Memeye/3ebd694b892896221b9eba28c5730c5d27be978b/client/src/assets/logo.png -------------------------------------------------------------------------------- /client/src/assets/memeye.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JerryC8080/Memeye/3ebd694b892896221b9eba28c5730c5d27be978b/client/src/assets/memeye.ico -------------------------------------------------------------------------------- /client/src/components/Memeye.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 166 | 167 | -------------------------------------------------------------------------------- /client/src/components/Panel.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 34 | 35 | 49 | 50 | -------------------------------------------------------------------------------- /client/src/components/PanelItem.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 20 | 21 | 48 | 49 | -------------------------------------------------------------------------------- /client/src/components/charts/OSCPUSUsedLine.js: -------------------------------------------------------------------------------- 1 | import { Line, mixins } from 'vue-chartjs'; 2 | const { reactiveProp } = mixins; 3 | 4 | const records = 100; 5 | 6 | let dynamicData = { 7 | labels: new Array(records), 8 | cpus: null, 9 | } 10 | 11 | let colorPool = [ 12 | '#F44336', // Red 13 | '#9C27B0', // Purple 14 | '#2196F3', // Blue 15 | '#8BC34A', // Light Green 16 | '#FFC107', // Amber 17 | '#CDDC39', // Lime 18 | '#00BCD4', // Cyan 19 | ]; 20 | 21 | function caculateData(cpus) { 22 | let now = new Date(); 23 | 24 | let data = { 25 | labels: dynamicData.labels, 26 | datasets: [], 27 | }; 28 | 29 | // Initialize cpus 30 | if (!dynamicData.cpus) { 31 | dynamicData.cpus = cpus.map(() => new Array(100)); 32 | } 33 | 34 | cpus.forEach(function (item, index) { 35 | let used = computedUsed(item.times); 36 | dynamicData.cpus[index].shift(); 37 | dynamicData.cpus[index].push(used); 38 | 39 | data.datasets.push({ 40 | label: `cpu ${index + 1}`, 41 | pointStyle: 'line', 42 | fill: false, 43 | borderColor: colorPool[index], 44 | backgroundColor: colorPool[index], 45 | data: dynamicData.cpus[index], 46 | }); 47 | }); 48 | 49 | return data; 50 | } 51 | 52 | function computedUsed(value) { 53 | var total = value.user + value.nice + value.sys + value.idle + value.irq; 54 | var cpu = ((total - value.idle) / total) * 100; 55 | return cpu; 56 | } 57 | 58 | let line = Line.extend({ 59 | mixins: [reactiveProp], 60 | props: ['options'], 61 | mounted() { 62 | this.renderChart(this.chartData, { 63 | title: { 64 | display: true, 65 | text: 'CPUs Used', 66 | }, 67 | tooltips: { 68 | callbacks: { 69 | label: (item, data) => { 70 | let label = data.datasets[item.datasetIndex].label; 71 | return label + ': ' + Math.floor(item.yLabel * 100) / 100 + ' %'; 72 | } 73 | } 74 | }, 75 | animation: false 76 | }) 77 | } 78 | }); 79 | 80 | line.caculateData = caculateData; 81 | 82 | export default line; 83 | -------------------------------------------------------------------------------- /client/src/components/charts/OSMemoryLine.js: -------------------------------------------------------------------------------- 1 | import { Line, mixins } from 'vue-chartjs' 2 | const { reactiveProp } = mixins 3 | 4 | const records = 100; 5 | 6 | let dynamicData = { 7 | labels: new Array(records), 8 | freeMemData: new Array(records), 9 | usedMemData: new Array(records), 10 | } 11 | 12 | function caculateData(type, value) { 13 | let now = new Date(); 14 | 15 | dynamicData[`${type}Data`].shift(); 16 | dynamicData[`${type}Data`].push(value / 1024 / 1024); 17 | 18 | return { 19 | labels: dynamicData.labels, 20 | datasets: [ 21 | { 22 | label: 'freeMem', 23 | pointStyle: 'line', 24 | borderColor: "rgba(54, 162, 235, 1)", 25 | backgroundColor: 'rgba(54, 162, 235, 0.4)', 26 | data: dynamicData.freeMemData, 27 | }, 28 | { 29 | label: 'usedMem', 30 | pointStyle: 'line', 31 | borderColor: "rgba(255, 99, 132, 1)", 32 | backgroundColor: 'rgba(255, 99, 132, 0.4)', 33 | data: dynamicData.usedMemData, 34 | } 35 | ] 36 | }; 37 | } 38 | 39 | let line = Line.extend({ 40 | mixins: [reactiveProp], 41 | props: ['options'], 42 | mounted() { 43 | this.renderChart(this.chartData, { 44 | title: { 45 | display: true, 46 | text: 'OS Memory Stat', 47 | }, 48 | tooltips: { 49 | callbacks: { 50 | label: (item) => (Math.floor(item.yLabel * 100) / 100 + ' MB') 51 | } 52 | }, 53 | animation: false 54 | }) 55 | } 56 | }); 57 | 58 | line.caculateData = caculateData; 59 | 60 | export default line; 61 | 62 | -------------------------------------------------------------------------------- /client/src/components/charts/OSMemoryPie.js: -------------------------------------------------------------------------------- 1 | import { Pie, mixins } from 'vue-chartjs'; 2 | const { reactiveProp } = mixins; 3 | 4 | function caculateData(freeMem, usedMem) { 5 | return { 6 | labels: [ 7 | "freeMem", 8 | "usedMem", 9 | ], 10 | datasets: [ 11 | { 12 | data: [freeMem, usedMem], 13 | backgroundColor: [ 14 | "#36A2EB", 15 | "#FF6384", 16 | ], 17 | hoverBackgroundColor: [ 18 | "#36A2EB", 19 | "#FF6384", 20 | ] 21 | }] 22 | }; 23 | } 24 | 25 | let pie = Pie.extend({ 26 | mixins: [reactiveProp], 27 | props: ['options'], 28 | mounted() { 29 | this.renderChart(this.chartData, { 30 | title: { 31 | display: true, 32 | text: 'OS Memory Stat', 33 | }, 34 | tooltips: { 35 | callbacks: { 36 | label: (item, data) => { 37 | let val = data.datasets[item.datasetIndex].data[item.index]; 38 | return Math.floor(val / 1024 / 1024 * 100) / 100 + ' MB'; 39 | } 40 | } 41 | }, 42 | animation: false, 43 | }); 44 | } 45 | }); 46 | pie.caculateData = caculateData; 47 | 48 | export default pie; -------------------------------------------------------------------------------- /client/src/components/charts/ProcessDoughnut.js: -------------------------------------------------------------------------------- 1 | import { Doughnut, mixins } from 'vue-chartjs' 2 | const { reactiveProp } = mixins 3 | 4 | function caculateData(heapFree, heapUsed) { 5 | heapFree = heapFree / 1024 / 1024; 6 | heapUsed = heapUsed / 1024 / 1024; 7 | 8 | return { 9 | labels: [ 10 | "heapFree", 11 | "heapUsed", 12 | ], 13 | datasets: [ 14 | { 15 | data: [heapFree, heapUsed], 16 | backgroundColor: [ 17 | "#36A2EB", 18 | "#FF6384", 19 | ], 20 | hoverBackgroundColor: [ 21 | "#36A2EB", 22 | "#FF6384", 23 | ] 24 | }] 25 | }; 26 | } 27 | 28 | let doughnut = Doughnut.extend({ 29 | mixins: [reactiveProp], 30 | props: ['options'], 31 | mounted() { 32 | this.renderChart(this.chartData, { 33 | title: { 34 | display: true, 35 | text: 'Process Memory Stat', 36 | }, 37 | tooltips: { 38 | callbacks: { 39 | label: (item, data) => { 40 | let val = data.datasets[item.datasetIndex].data[item.index]; 41 | return Math.floor(val * 100) / 100 + ' MB'; 42 | } 43 | } 44 | }, 45 | animation: false, 46 | }) 47 | } 48 | }); 49 | 50 | doughnut.caculateData = caculateData; 51 | 52 | export default doughnut; -------------------------------------------------------------------------------- /client/src/components/charts/ProcessLine.js: -------------------------------------------------------------------------------- 1 | import { Line, mixins } from 'vue-chartjs' 2 | const { reactiveProp } = mixins 3 | const records = 100; // second; 4 | 5 | let dynamicData = { 6 | labels: new Array(records), 7 | rssData: new Array(records), 8 | heapTotalData: new Array(records), 9 | heapUsedData: new Array(records), 10 | } 11 | 12 | function caculateData({ type, value }) { 13 | 14 | // dynamicData.labels.shift(); 15 | // dynamicData.labels.push(new Date()); 16 | 17 | dynamicData[`${type}Data`].shift(); 18 | dynamicData[`${type}Data`].push(value / 1024 / 1024); 19 | 20 | return { 21 | labels: dynamicData.labels, 22 | datasets: [ 23 | { 24 | label: 'heapUsed', 25 | pointStyle: 'line', 26 | // fill: false, 27 | borderColor: "rgba(255, 99, 132, 1)", 28 | backgroundColor: 'rgba(255, 99, 132, 0.4)', 29 | data: dynamicData.heapUsedData, 30 | }, 31 | { 32 | label: 'heapTotal', 33 | pointStyle: 'line', 34 | // fill: false, 35 | borderColor: "rgba(54, 162, 235, 1)", 36 | backgroundColor: 'rgba(54, 162, 235, 0.4)', 37 | data: dynamicData.heapTotalData, 38 | }, 39 | { 40 | label: 'rss', 41 | pointStyle: 'line', 42 | // fill: false, 43 | borderColor: "rgba(53, 221, 101, 1)", 44 | backgroundColor: 'rgba(53, 221, 101, 0.4)', 45 | data: dynamicData.rssData, 46 | }, 47 | ] 48 | }; 49 | } 50 | 51 | let line = Line.extend({ 52 | mixins: [reactiveProp], 53 | props: ['options'], 54 | mounted() { 55 | this.renderChart(this.chartData, { 56 | title: { 57 | display: true, 58 | text: 'Process Memory Stat', 59 | }, 60 | tooltips: { 61 | callbacks: { 62 | label: (item) => (Math.floor(item.yLabel * 100) / 100 + ' MB') 63 | } 64 | }, 65 | animation: false 66 | }) 67 | } 68 | }); 69 | 70 | line.caculateData = caculateData; 71 | 72 | export default line; 73 | 74 | -------------------------------------------------------------------------------- /client/src/components/charts/V8HeapSpaceBar.js: -------------------------------------------------------------------------------- 1 | import { Bar, mixins } from 'vue-chartjs'; 2 | const { reactiveProp } = mixins; 3 | 4 | let space = { 5 | newSpace: 0, 6 | oldSpace: 0, 7 | codeSpace: 0, 8 | mapSpace: 0, 9 | largeObjectSpace: 0, 10 | } 11 | 12 | function caculateData(type, val) { 13 | space[type] = val; 14 | return { 15 | labels: [ 16 | 'newSpace', 17 | 'oldSpace', 18 | 'codeSpace', 19 | 'mapSpace', 20 | 'largeObjectSpace', 21 | ], 22 | datasets: [ 23 | { 24 | label: 'Space Used Percent', 25 | backgroundColor: [ 26 | '#9CCC65', 27 | // 'rgba(156, 204, 101, 0.8)', 28 | '#42A5F5', 29 | // 'rgba(33, 150, 243, 0.8)', 30 | '#5C6BC0', 31 | '#FFCA28', 32 | '#8D6E63', 33 | ], 34 | borderColor: [ 35 | '#9CCC65', 36 | '#42A5F5', 37 | '#5C6BC0', 38 | '#FFCA28', 39 | '#8D6E63', 40 | ], 41 | borderWidth: 2, 42 | data: [ 43 | space.newSpace, 44 | space.oldSpace, 45 | space.codeSpace, 46 | space.mapSpace, 47 | space.largeObjectSpace, 48 | ], 49 | }, 50 | ] 51 | }; 52 | } 53 | 54 | let bar = Bar.extend({ 55 | mixins: [reactiveProp], 56 | props: ['options'], 57 | mounted() { 58 | this.renderChart(this.chartData, { 59 | legend: { 60 | display: false, 61 | }, 62 | tooltips: { 63 | callbacks: { 64 | label: function (item, data) { 65 | return Math.floor(item.xLabel * 100) / 100 + ' %'; 66 | } 67 | } 68 | }, 69 | scales: { 70 | xAxes: [{ 71 | ticks: { 72 | min: 0, 73 | max: 100 74 | } 75 | }] 76 | }, 77 | title: { 78 | display: true, 79 | text: 'Space Used Percent', 80 | }, 81 | animation: false, 82 | }, 'horizontalBar'); 83 | } 84 | }); 85 | bar.caculateData = caculateData; 86 | 87 | export default bar; -------------------------------------------------------------------------------- /client/src/components/charts/V8HeapSpacePie.js: -------------------------------------------------------------------------------- 1 | import { Pie, mixins } from 'vue-chartjs'; 2 | const { reactiveProp } = mixins; 3 | 4 | let space = { 5 | newSpace: 0, 6 | oldSpace: 0, 7 | codeSpace: 0, 8 | mapSpace: 0, 9 | largeObjectSpace: 0, 10 | } 11 | 12 | function caculateData(type, val) { 13 | space[type] = val; 14 | return { 15 | labels: [ 16 | 'newSpace', 17 | 'oldSpace', 18 | 'codeSpace', 19 | 'mapSpace', 20 | 'largeObjectSpace', 21 | ], 22 | datasets: [ 23 | { 24 | data: [ 25 | space.newSpace, 26 | space.oldSpace, 27 | space.codeSpace, 28 | space.mapSpace, 29 | space.largeObjectSpace, 30 | ], 31 | backgroundColor: [ 32 | '#9CCC65', 33 | '#42A5F5', 34 | '#5C6BC0', 35 | '#FFCA28', 36 | '#8D6E63', 37 | ], 38 | hoverBackgroundColor: [ 39 | '#7CB342', 40 | '#1E88E5', 41 | '#3949AB', 42 | '#FFB300', 43 | '#6D4C41', 44 | ] 45 | }] 46 | }; 47 | } 48 | 49 | let pie = Pie.extend({ 50 | mixins: [reactiveProp], 51 | props: ['options'], 52 | mounted() { 53 | this.renderChart(this.chartData, { 54 | title: { 55 | display: true, 56 | text: 'V8 Heap Sapce', 57 | }, 58 | tooltips: { 59 | callbacks: { 60 | label: (item, data) => { 61 | let val = data.datasets[item.datasetIndex].data[item.index]; 62 | return Math.floor(val / 1024 * 100) / 100 + ' KB'; 63 | } 64 | } 65 | }, 66 | animation: false, 67 | }); 68 | } 69 | }); 70 | pie.caculateData = caculateData; 71 | 72 | export default pie; -------------------------------------------------------------------------------- /client/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | window.vm = new Vue({ 5 | el: '#app', 6 | render: h => h(App) 7 | }) -------------------------------------------------------------------------------- /client/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var webpack = require('webpack') 3 | 4 | let config = { 5 | entry: './src/main.js', 6 | output: { 7 | path: path.resolve(__dirname, './dist'), 8 | publicPath: '/dist/', 9 | filename: 'build.js' 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.vue$/, 15 | loader: 'vue-loader', 16 | options: { 17 | loaders: { 18 | } 19 | // other vue-loader options go here 20 | } 21 | }, 22 | { 23 | test: /\.js$/, 24 | loader: 'babel-loader', 25 | exclude: /node_modules/ 26 | }, 27 | { 28 | test: /\.(png|jpg|gif|svg)$/, 29 | loader: 'file-loader', 30 | options: { 31 | name: '[name].[ext]?[hash]' 32 | } 33 | } 34 | ] 35 | }, 36 | resolve: { 37 | alias: { 38 | 'vue$': 'vue/dist/vue.esm.js' 39 | } 40 | }, 41 | devServer: { 42 | historyApiFallback: true, 43 | noInfo: true 44 | }, 45 | performance: { 46 | hints: false 47 | }, 48 | devtool: '#eval-source-map' 49 | } 50 | 51 | if (process.env.NODE_ENV === 'production') { 52 | config.output.path = path.resolve(__dirname, '../assets/dist'), 53 | module.exports.devtool = '#source-map' 54 | // http://vue-loader.vuejs.org/en/workflow/production.html 55 | module.exports.plugins = (module.exports.plugins || []).concat([ 56 | new webpack.DefinePlugin({ 57 | 'process.env': { 58 | NODE_ENV: '"production"' 59 | } 60 | }), 61 | new webpack.optimize.UglifyJsPlugin({ 62 | sourceMap: true, 63 | compress: { 64 | warnings: false 65 | } 66 | }), 67 | new webpack.LoaderOptionsPlugin({ 68 | minimize: true 69 | }) 70 | ]) 71 | } 72 | 73 | module.exports = config; 74 | -------------------------------------------------------------------------------- /coverage/coverage.json: -------------------------------------------------------------------------------- 1 | {"/Users/JerryC/Workspace/jc-repository/Memeye/index.js":{"path":"/Users/JerryC/Workspace/jc-repository/Memeye/index.js","s":{"1":1},"b":{},"f":{},"fnMap":{},"statementMap":{"1":{"start":{"line":1,"column":0},"end":{"line":1,"column":43}}},"branchMap":{}},"/Users/JerryC/Workspace/jc-repository/Memeye/src/index.js":{"path":"/Users/JerryC/Workspace/jc-repository/Memeye/src/index.js","s":{"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":0,"16":1,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0},"b":{"1":[1,0]},"f":{"1":1},"fnMap":{"1":{"name":"(anonymous_1)","line":14,"loc":{"start":{"line":14,"column":17},"end":{"line":14,"column":55}}}},"statementMap":{"1":{"start":{"line":8,"column":0},"end":{"line":8,"column":29}},"2":{"start":{"line":9,"column":0},"end":{"line":9,"column":47}},"3":{"start":{"line":10,"column":0},"end":{"line":10,"column":48}},"4":{"start":{"line":11,"column":0},"end":{"line":11,"column":55}},"5":{"start":{"line":12,"column":0},"end":{"line":12,"column":42}},"6":{"start":{"line":14,"column":0},"end":{"line":43,"column":1}},"7":{"start":{"line":15,"column":4},"end":{"line":15,"column":34}},"8":{"start":{"line":15,"column":13},"end":{"line":15,"column":34}},"9":{"start":{"line":17,"column":4},"end":{"line":17,"column":43}},"10":{"start":{"line":19,"column":4},"end":{"line":19,"column":66}},"11":{"start":{"line":21,"column":4},"end":{"line":21,"column":49}},"12":{"start":{"line":23,"column":4},"end":{"line":23,"column":52}},"13":{"start":{"line":24,"column":4},"end":{"line":24,"column":22}},"14":{"start":{"line":27,"column":4},"end":{"line":27,"column":54}},"15":{"start":{"line":27,"column":29},"end":{"line":27,"column":52}},"16":{"start":{"line":30,"column":4},"end":{"line":42,"column":7}},"17":{"start":{"line":33,"column":8},"end":{"line":33,"column":75}},"18":{"start":{"line":34,"column":8},"end":{"line":34,"column":66}},"19":{"start":{"line":35,"column":8},"end":{"line":35,"column":32}},"20":{"start":{"line":39,"column":8},"end":{"line":39,"column":65}},"21":{"start":{"line":40,"column":8},"end":{"line":40,"column":52}},"22":{"start":{"line":41,"column":8},"end":{"line":41,"column":25}}},"branchMap":{"1":{"line":15,"type":"if","locations":[{"start":{"line":15,"column":4},"end":{"line":15,"column":4}},{"start":{"line":15,"column":4},"end":{"line":15,"column":4}}]}}},"/Users/JerryC/Workspace/jc-repository/Memeye/src/lib/Collector.js":{"path":"/Users/JerryC/Workspace/jc-repository/Memeye/src/lib/Collector.js","s":{"1":1,"2":1,"3":1,"4":1,"5":2,"6":2,"7":2,"8":1,"9":1,"10":1,"11":3,"12":3,"13":1,"14":1,"15":2,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1},"b":{"1":[1,2],"2":[1,0],"3":[1,0]},"f":{"1":2,"2":1,"3":3},"fnMap":{"1":{"name":"Collector","line":10,"loc":{"start":{"line":10,"column":0},"end":{"line":10,"column":57}}},"2":{"name":"(anonymous_2)","line":16,"loc":{"start":{"line":16,"column":27},"end":{"line":16,"column":39}}},"3":{"name":"(anonymous_3)","line":20,"loc":{"start":{"line":20,"column":28},"end":{"line":20,"column":40}}}},"statementMap":{"1":{"start":{"line":6,"column":0},"end":{"line":6,"column":25}},"2":{"start":{"line":7,"column":0},"end":{"line":7,"column":25}},"3":{"start":{"line":8,"column":0},"end":{"line":8,"column":38}},"4":{"start":{"line":10,"column":0},"end":{"line":14,"column":1}},"5":{"start":{"line":11,"column":4},"end":{"line":11,"column":31}},"6":{"start":{"line":12,"column":4},"end":{"line":12,"column":25}},"7":{"start":{"line":13,"column":4},"end":{"line":13,"column":31}},"8":{"start":{"line":16,"column":0},"end":{"line":18,"column":1}},"9":{"start":{"line":17,"column":4},"end":{"line":17,"column":33}},"10":{"start":{"line":20,"column":0},"end":{"line":66,"column":1}},"11":{"start":{"line":21,"column":4},"end":{"line":21,"column":20}},"12":{"start":{"line":23,"column":4},"end":{"line":26,"column":5}},"13":{"start":{"line":24,"column":8},"end":{"line":24,"column":52}},"14":{"start":{"line":25,"column":8},"end":{"line":25,"column":15}},"15":{"start":{"line":28,"column":4},"end":{"line":65,"column":23}},"16":{"start":{"line":31,"column":8},"end":{"line":31,"column":48}},"17":{"start":{"line":32,"column":8},"end":{"line":35,"column":9}},"18":{"start":{"line":36,"column":8},"end":{"line":40,"column":9}},"19":{"start":{"line":41,"column":8},"end":{"line":54,"column":9}},"20":{"start":{"line":57,"column":8},"end":{"line":59,"column":9}},"21":{"start":{"line":58,"column":12},"end":{"line":58,"column":38}},"22":{"start":{"line":61,"column":8},"end":{"line":63,"column":9}},"23":{"start":{"line":62,"column":12},"end":{"line":62,"column":145}},"24":{"start":{"line":68,"column":0},"end":{"line":68,"column":27}}},"branchMap":{"1":{"line":23,"type":"if","locations":[{"start":{"line":23,"column":4},"end":{"line":23,"column":4}},{"start":{"line":23,"column":4},"end":{"line":23,"column":4}}]},"2":{"line":57,"type":"if","locations":[{"start":{"line":57,"column":8},"end":{"line":57,"column":8}},{"start":{"line":57,"column":8},"end":{"line":57,"column":8}}]},"3":{"line":61,"type":"if","locations":[{"start":{"line":61,"column":8},"end":{"line":61,"column":8}},{"start":{"line":61,"column":8},"end":{"line":61,"column":8}}]}}},"/Users/JerryC/Workspace/jc-repository/Memeye/src/lib/Logger.js":{"path":"/Users/JerryC/Workspace/jc-repository/Memeye/src/lib/Logger.js","s":{"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":2,"11":0,"12":2,"13":1,"14":2,"15":1,"16":0,"17":0,"18":1,"19":0,"20":0,"21":1,"22":2,"23":2,"24":1,"25":1,"26":1,"27":1},"b":{"1":[0,2],"2":[2,1],"3":[1,1],"4":[0,0],"5":[0,0],"6":[2,0],"7":[1,0]},"f":{"1":1,"2":2,"3":0,"4":0,"5":2,"6":1},"fnMap":{"1":{"name":"Logger","line":19,"loc":{"start":{"line":19,"column":0},"end":{"line":19,"column":40}}},"2":{"name":"(anonymous_2)","line":25,"loc":{"start":{"line":25,"column":28},"end":{"line":25,"column":45}}},"3":{"name":"(anonymous_3)","line":31,"loc":{"start":{"line":31,"column":25},"end":{"line":31,"column":40}}},"4":{"name":"(anonymous_4)","line":35,"loc":{"start":{"line":35,"column":24},"end":{"line":35,"column":39}}},"5":{"name":"(anonymous_5)","line":39,"loc":{"start":{"line":39,"column":24},"end":{"line":39,"column":39}}},"6":{"name":"(anonymous_6)","line":43,"loc":{"start":{"line":43,"column":25},"end":{"line":43,"column":40}}}},"statementMap":{"1":{"start":{"line":1,"column":0},"end":{"line":1,"column":38}},"2":{"start":{"line":3,"column":0},"end":{"line":8,"column":3}},"3":{"start":{"line":10,"column":0},"end":{"line":15,"column":1}},"4":{"start":{"line":17,"column":0},"end":{"line":17,"column":24}},"5":{"start":{"line":19,"column":0},"end":{"line":23,"column":1}},"6":{"start":{"line":20,"column":4},"end":{"line":20,"column":36}},"7":{"start":{"line":21,"column":4},"end":{"line":21,"column":25}},"8":{"start":{"line":22,"column":4},"end":{"line":22,"column":29}},"9":{"start":{"line":25,"column":0},"end":{"line":29,"column":1}},"10":{"start":{"line":26,"column":4},"end":{"line":26,"column":74}},"11":{"start":{"line":26,"column":64},"end":{"line":26,"column":74}},"12":{"start":{"line":27,"column":4},"end":{"line":27,"column":59}},"13":{"start":{"line":27,"column":35},"end":{"line":27,"column":59}},"14":{"start":{"line":28,"column":4},"end":{"line":28,"column":30}},"15":{"start":{"line":31,"column":0},"end":{"line":33,"column":1}},"16":{"start":{"line":32,"column":4},"end":{"line":32,"column":88}},"17":{"start":{"line":32,"column":41},"end":{"line":32,"column":88}},"18":{"start":{"line":35,"column":0},"end":{"line":37,"column":1}},"19":{"start":{"line":36,"column":4},"end":{"line":36,"column":86}},"20":{"start":{"line":36,"column":40},"end":{"line":36,"column":86}},"21":{"start":{"line":39,"column":0},"end":{"line":41,"column":1}},"22":{"start":{"line":40,"column":4},"end":{"line":40,"column":86}},"23":{"start":{"line":40,"column":40},"end":{"line":40,"column":86}},"24":{"start":{"line":43,"column":0},"end":{"line":45,"column":1}},"25":{"start":{"line":44,"column":4},"end":{"line":44,"column":88}},"26":{"start":{"line":44,"column":41},"end":{"line":44,"column":88}},"27":{"start":{"line":47,"column":0},"end":{"line":47,"column":44}}},"branchMap":{"1":{"line":26,"type":"if","locations":[{"start":{"line":26,"column":4},"end":{"line":26,"column":4}},{"start":{"line":26,"column":4},"end":{"line":26,"column":4}}]},"2":{"line":26,"type":"binary-expr","locations":[{"start":{"line":26,"column":8},"end":{"line":26,"column":33}},{"start":{"line":26,"column":37},"end":{"line":26,"column":62}}]},"3":{"line":27,"type":"if","locations":[{"start":{"line":27,"column":4},"end":{"line":27,"column":4}},{"start":{"line":27,"column":4},"end":{"line":27,"column":4}}]},"4":{"line":32,"type":"if","locations":[{"start":{"line":32,"column":4},"end":{"line":32,"column":4}},{"start":{"line":32,"column":4},"end":{"line":32,"column":4}}]},"5":{"line":36,"type":"if","locations":[{"start":{"line":36,"column":4},"end":{"line":36,"column":4}},{"start":{"line":36,"column":4},"end":{"line":36,"column":4}}]},"6":{"line":40,"type":"if","locations":[{"start":{"line":40,"column":4},"end":{"line":40,"column":4}},{"start":{"line":40,"column":4},"end":{"line":40,"column":4}}]},"7":{"line":44,"type":"if","locations":[{"start":{"line":44,"column":4},"end":{"line":44,"column":4}},{"start":{"line":44,"column":4},"end":{"line":44,"column":4}}]}}},"/Users/JerryC/Workspace/jc-repository/Memeye/src/lib/Indicators.js":{"path":"/Users/JerryC/Workspace/jc-repository/Memeye/src/lib/Indicators.js","s":{"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":2,"11":2,"12":2,"13":2,"14":2,"15":2,"16":2,"17":1,"18":2,"19":1},"b":{},"f":{"1":1,"2":2,"3":2,"4":1},"fnMap":{"1":{"name":"Indicators","line":11,"loc":{"start":{"line":11,"column":0},"end":{"line":11,"column":32}}},"2":{"name":"setWatcher","line":26,"loc":{"start":{"line":26,"column":34},"end":{"line":26,"column":66}}},"3":{"name":"(anonymous_3)","line":34,"loc":{"start":{"line":34,"column":17},"end":{"line":34,"column":34}}},"4":{"name":"(anonymous_4)","line":38,"loc":{"start":{"line":38,"column":17},"end":{"line":38,"column":29}}}},"statementMap":{"1":{"start":{"line":7,"column":0},"end":{"line":7,"column":33}},"2":{"start":{"line":8,"column":0},"end":{"line":8,"column":29}},"3":{"start":{"line":11,"column":0},"end":{"line":20,"column":1}},"4":{"start":{"line":12,"column":4},"end":{"line":12,"column":35}},"5":{"start":{"line":15,"column":4},"end":{"line":15,"column":20}},"6":{"start":{"line":16,"column":4},"end":{"line":16,"column":42}},"7":{"start":{"line":19,"column":4},"end":{"line":19,"column":43}},"8":{"start":{"line":23,"column":0},"end":{"line":23,"column":47}},"9":{"start":{"line":26,"column":0},"end":{"line":46,"column":1}},"10":{"start":{"line":27,"column":4},"end":{"line":27,"column":24}},"11":{"start":{"line":28,"column":4},"end":{"line":28,"column":20}},"12":{"start":{"line":31,"column":4},"end":{"line":43,"column":7}},"13":{"start":{"line":32,"column":8},"end":{"line":32,"column":42}},"14":{"start":{"line":33,"column":8},"end":{"line":42,"column":9}},"15":{"start":{"line":35,"column":16},"end":{"line":35,"column":50}},"16":{"start":{"line":36,"column":16},"end":{"line":36,"column":40}},"17":{"start":{"line":39,"column":16},"end":{"line":39,"column":39}},"18":{"start":{"line":45,"column":4},"end":{"line":45,"column":46}},"19":{"start":{"line":48,"column":0},"end":{"line":48,"column":28}}},"branchMap":{}}} -------------------------------------------------------------------------------- /coverage/lcov-report/base.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | margin:0; padding: 0; 3 | height: 100%; 4 | } 5 | body { 6 | font-family: Helvetica Neue, Helvetica, Arial; 7 | font-size: 14px; 8 | color:#333; 9 | } 10 | .small { font-size: 12px; } 11 | *, *:after, *:before { 12 | -webkit-box-sizing:border-box; 13 | -moz-box-sizing:border-box; 14 | box-sizing:border-box; 15 | } 16 | h1 { font-size: 20px; margin: 0;} 17 | h2 { font-size: 14px; } 18 | pre { 19 | font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; 20 | margin: 0; 21 | padding: 0; 22 | -moz-tab-size: 2; 23 | -o-tab-size: 2; 24 | tab-size: 2; 25 | } 26 | a { color:#0074D9; text-decoration:none; } 27 | a:hover { text-decoration:underline; } 28 | .strong { font-weight: bold; } 29 | .space-top1 { padding: 10px 0 0 0; } 30 | .pad2y { padding: 20px 0; } 31 | .pad1y { padding: 10px 0; } 32 | .pad2x { padding: 0 20px; } 33 | .pad2 { padding: 20px; } 34 | .pad1 { padding: 10px; } 35 | .space-left2 { padding-left:55px; } 36 | .space-right2 { padding-right:20px; } 37 | .center { text-align:center; } 38 | .clearfix { display:block; } 39 | .clearfix:after { 40 | content:''; 41 | display:block; 42 | height:0; 43 | clear:both; 44 | visibility:hidden; 45 | } 46 | .fl { float: left; } 47 | @media only screen and (max-width:640px) { 48 | .col3 { width:100%; max-width:100%; } 49 | .hide-mobile { display:none!important; } 50 | } 51 | 52 | .quiet { 53 | color: #7f7f7f; 54 | color: rgba(0,0,0,0.5); 55 | } 56 | .quiet a { opacity: 0.7; } 57 | 58 | .fraction { 59 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; 60 | font-size: 10px; 61 | color: #555; 62 | background: #E8E8E8; 63 | padding: 4px 5px; 64 | border-radius: 3px; 65 | vertical-align: middle; 66 | } 67 | 68 | div.path a:link, div.path a:visited { color: #333; } 69 | table.coverage { 70 | border-collapse: collapse; 71 | margin: 10px 0 0 0; 72 | padding: 0; 73 | } 74 | 75 | table.coverage td { 76 | margin: 0; 77 | padding: 0; 78 | vertical-align: top; 79 | } 80 | table.coverage td.line-count { 81 | text-align: right; 82 | padding: 0 5px 0 20px; 83 | } 84 | table.coverage td.line-coverage { 85 | text-align: right; 86 | padding-right: 10px; 87 | min-width:20px; 88 | } 89 | 90 | table.coverage td span.cline-any { 91 | display: inline-block; 92 | padding: 0 5px; 93 | width: 100%; 94 | } 95 | .missing-if-branch { 96 | display: inline-block; 97 | margin-right: 5px; 98 | border-radius: 3px; 99 | position: relative; 100 | padding: 0 4px; 101 | background: #333; 102 | color: yellow; 103 | } 104 | 105 | .skip-if-branch { 106 | display: none; 107 | margin-right: 10px; 108 | position: relative; 109 | padding: 0 4px; 110 | background: #ccc; 111 | color: white; 112 | } 113 | .missing-if-branch .typ, .skip-if-branch .typ { 114 | color: inherit !important; 115 | } 116 | .coverage-summary { 117 | border-collapse: collapse; 118 | width: 100%; 119 | } 120 | .coverage-summary tr { border-bottom: 1px solid #bbb; } 121 | .keyline-all { border: 1px solid #ddd; } 122 | .coverage-summary td, .coverage-summary th { padding: 10px; } 123 | .coverage-summary tbody { border: 1px solid #bbb; } 124 | .coverage-summary td { border-right: 1px solid #bbb; } 125 | .coverage-summary td:last-child { border-right: none; } 126 | .coverage-summary th { 127 | text-align: left; 128 | font-weight: normal; 129 | white-space: nowrap; 130 | } 131 | .coverage-summary th.file { border-right: none !important; } 132 | .coverage-summary th.pct { } 133 | .coverage-summary th.pic, 134 | .coverage-summary th.abs, 135 | .coverage-summary td.pct, 136 | .coverage-summary td.abs { text-align: right; } 137 | .coverage-summary td.file { white-space: nowrap; } 138 | .coverage-summary td.pic { min-width: 120px !important; } 139 | .coverage-summary tfoot td { } 140 | 141 | .coverage-summary .sorter { 142 | height: 10px; 143 | width: 7px; 144 | display: inline-block; 145 | margin-left: 0.5em; 146 | background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; 147 | } 148 | .coverage-summary .sorted .sorter { 149 | background-position: 0 -20px; 150 | } 151 | .coverage-summary .sorted-desc .sorter { 152 | background-position: 0 -10px; 153 | } 154 | .status-line { height: 10px; } 155 | /* dark red */ 156 | .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } 157 | .low .chart { border:1px solid #C21F39 } 158 | /* medium red */ 159 | .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } 160 | /* light red */ 161 | .low, .cline-no { background:#FCE1E5 } 162 | /* light green */ 163 | .high, .cline-yes { background:rgb(230,245,208) } 164 | /* medium green */ 165 | .cstat-yes { background:rgb(161,215,106) } 166 | /* dark green */ 167 | .status-line.high, .high .cover-fill { background:rgb(77,146,33) } 168 | .high .chart { border:1px solid rgb(77,146,33) } 169 | /* dark yellow (gold) */ 170 | .medium .chart { border:1px solid #f9cd0b; } 171 | .status-line.medium, .medium .cover-fill { background: #f9cd0b; } 172 | /* light yellow */ 173 | .medium { background: #fff4c2; } 174 | /* light gray */ 175 | span.cline-neutral { background: #eaeaea; } 176 | 177 | .cbranch-no { background: yellow !important; color: #111; } 178 | 179 | .cstat-skip { background: #ddd; color: #111; } 180 | .fstat-skip { background: #ddd; color: #111 !important; } 181 | .cbranch-skip { background: #ddd !important; color: #111; } 182 | 183 | 184 | .cover-fill, .cover-empty { 185 | display:inline-block; 186 | height: 12px; 187 | } 188 | .chart { 189 | line-height: 0; 190 | } 191 | .cover-empty { 192 | background: white; 193 | } 194 | .cover-full { 195 | border-right: none !important; 196 | } 197 | pre.prettyprint { 198 | border: none !important; 199 | padding: 0 !important; 200 | margin: 0 !important; 201 | } 202 | .com { color: #999 !important; } 203 | .ignore-none { color: #999; font-weight: normal; } 204 | 205 | .wrapper { 206 | min-height: 100%; 207 | height: auto !important; 208 | height: 100%; 209 | margin: 0 auto -48px; 210 | } 211 | .footer, .push { 212 | height: 48px; 213 | } 214 | -------------------------------------------------------------------------------- /coverage/lcov-report/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for All files 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | / 20 |

21 |
22 |
23 | 87.1% 24 | Statements 25 | 81/93 26 |
27 |
28 | 54.55% 29 | Branches 30 | 12/22 31 |
32 |
33 | 85.71% 34 | Functions 35 | 12/14 36 |
37 |
38 | 90.59% 39 | Lines 40 | 77/85 41 |
42 |
43 |
44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 |
FileStatementsBranchesFunctionsLines
Memeye/
100%1/1100%0/0100%0/0100%1/1
Memeye/src/
68.18%15/2250%1/2100%1/170%14/20
Memeye/src/lib/
92.86%65/7055%11/2084.62%11/1396.88%62/64
102 |
103 |
104 | 108 | 109 | 110 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /coverage/lcov-report/lib/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for lib/ 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | all files lib/ 20 |

21 |
22 |
23 | 100% 24 | Statements 25 | 38/38 26 |
27 |
28 | 100% 29 | Branches 30 | 2/2 31 |
32 |
33 | 100% 34 | Functions 35 | 7/7 36 |
37 |
38 | 100% 39 | Lines 40 | 38/38 41 |
42 |
43 |
44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 |
FileStatementsBranchesFunctionsLines
Collector.js
100%19/19100%2/2100%3/3100%19/19
Indicators.js
100%19/19100%0/0100%4/4100%19/19
89 |
90 |
91 | 95 | 96 | 97 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /coverage/lcov-report/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} 2 | -------------------------------------------------------------------------------- /coverage/lcov-report/prettify.js: -------------------------------------------------------------------------------- 1 | window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); 2 | -------------------------------------------------------------------------------- /coverage/lcov-report/sort-arrow-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JerryC8080/Memeye/3ebd694b892896221b9eba28c5730c5d27be978b/coverage/lcov-report/sort-arrow-sprite.png -------------------------------------------------------------------------------- /coverage/lcov-report/sorter.js: -------------------------------------------------------------------------------- 1 | var addSorting = (function () { 2 | "use strict"; 3 | var cols, 4 | currentSort = { 5 | index: 0, 6 | desc: false 7 | }; 8 | 9 | // returns the summary table element 10 | function getTable() { return document.querySelector('.coverage-summary'); } 11 | // returns the thead element of the summary table 12 | function getTableHeader() { return getTable().querySelector('thead tr'); } 13 | // returns the tbody element of the summary table 14 | function getTableBody() { return getTable().querySelector('tbody'); } 15 | // returns the th element for nth column 16 | function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; } 17 | 18 | // loads all columns 19 | function loadColumns() { 20 | var colNodes = getTableHeader().querySelectorAll('th'), 21 | colNode, 22 | cols = [], 23 | col, 24 | i; 25 | 26 | for (i = 0; i < colNodes.length; i += 1) { 27 | colNode = colNodes[i]; 28 | col = { 29 | key: colNode.getAttribute('data-col'), 30 | sortable: !colNode.getAttribute('data-nosort'), 31 | type: colNode.getAttribute('data-type') || 'string' 32 | }; 33 | cols.push(col); 34 | if (col.sortable) { 35 | col.defaultDescSort = col.type === 'number'; 36 | colNode.innerHTML = colNode.innerHTML + ''; 37 | } 38 | } 39 | return cols; 40 | } 41 | // attaches a data attribute to every tr element with an object 42 | // of data values keyed by column name 43 | function loadRowData(tableRow) { 44 | var tableCols = tableRow.querySelectorAll('td'), 45 | colNode, 46 | col, 47 | data = {}, 48 | i, 49 | val; 50 | for (i = 0; i < tableCols.length; i += 1) { 51 | colNode = tableCols[i]; 52 | col = cols[i]; 53 | val = colNode.getAttribute('data-value'); 54 | if (col.type === 'number') { 55 | val = Number(val); 56 | } 57 | data[col.key] = val; 58 | } 59 | return data; 60 | } 61 | // loads all row data 62 | function loadData() { 63 | var rows = getTableBody().querySelectorAll('tr'), 64 | i; 65 | 66 | for (i = 0; i < rows.length; i += 1) { 67 | rows[i].data = loadRowData(rows[i]); 68 | } 69 | } 70 | // sorts the table using the data for the ith column 71 | function sortByIndex(index, desc) { 72 | var key = cols[index].key, 73 | sorter = function (a, b) { 74 | a = a.data[key]; 75 | b = b.data[key]; 76 | return a < b ? -1 : a > b ? 1 : 0; 77 | }, 78 | finalSorter = sorter, 79 | tableBody = document.querySelector('.coverage-summary tbody'), 80 | rowNodes = tableBody.querySelectorAll('tr'), 81 | rows = [], 82 | i; 83 | 84 | if (desc) { 85 | finalSorter = function (a, b) { 86 | return -1 * sorter(a, b); 87 | }; 88 | } 89 | 90 | for (i = 0; i < rowNodes.length; i += 1) { 91 | rows.push(rowNodes[i]); 92 | tableBody.removeChild(rowNodes[i]); 93 | } 94 | 95 | rows.sort(finalSorter); 96 | 97 | for (i = 0; i < rows.length; i += 1) { 98 | tableBody.appendChild(rows[i]); 99 | } 100 | } 101 | // removes sort indicators for current column being sorted 102 | function removeSortIndicators() { 103 | var col = getNthColumn(currentSort.index), 104 | cls = col.className; 105 | 106 | cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); 107 | col.className = cls; 108 | } 109 | // adds sort indicators for current column being sorted 110 | function addSortIndicators() { 111 | getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted'; 112 | } 113 | // adds event listeners for all sorter widgets 114 | function enableUI() { 115 | var i, 116 | el, 117 | ithSorter = function ithSorter(i) { 118 | var col = cols[i]; 119 | 120 | return function () { 121 | var desc = col.defaultDescSort; 122 | 123 | if (currentSort.index === i) { 124 | desc = !currentSort.desc; 125 | } 126 | sortByIndex(i, desc); 127 | removeSortIndicators(); 128 | currentSort.index = i; 129 | currentSort.desc = desc; 130 | addSortIndicators(); 131 | }; 132 | }; 133 | for (i =0 ; i < cols.length; i += 1) { 134 | if (cols[i].sortable) { 135 | // add the click event handler on the th so users 136 | // dont have to click on those tiny arrows 137 | el = getNthColumn(i).querySelector('.sorter').parentElement; 138 | if (el.addEventListener) { 139 | el.addEventListener('click', ithSorter(i)); 140 | } else { 141 | el.attachEvent('onclick', ithSorter(i)); 142 | } 143 | } 144 | } 145 | } 146 | // adds sorting functionality to the UI 147 | return function () { 148 | if (!getTable()) { 149 | return; 150 | } 151 | cols = loadColumns(); 152 | loadData(cols); 153 | addSortIndicators(); 154 | enableUI(); 155 | }; 156 | })(); 157 | 158 | window.addEventListener('load', addSorting); 159 | -------------------------------------------------------------------------------- /coverage/lcov.info: -------------------------------------------------------------------------------- 1 | TN: 2 | SF:/Users/JerryC/Workspace/jc-repository/Memeye/index.js 3 | FNF:0 4 | FNH:0 5 | DA:1,1 6 | LF:1 7 | LH:1 8 | BRF:0 9 | BRH:0 10 | end_of_record 11 | TN: 12 | SF:/Users/JerryC/Workspace/jc-repository/Memeye/src/index.js 13 | FN:14,(anonymous_1) 14 | FNF:1 15 | FNH:1 16 | FNDA:1,(anonymous_1) 17 | DA:8,1 18 | DA:9,1 19 | DA:10,1 20 | DA:11,1 21 | DA:12,1 22 | DA:14,1 23 | DA:15,1 24 | DA:17,1 25 | DA:19,1 26 | DA:21,1 27 | DA:23,1 28 | DA:24,1 29 | DA:27,1 30 | DA:30,1 31 | DA:33,0 32 | DA:34,0 33 | DA:35,0 34 | DA:39,0 35 | DA:40,0 36 | DA:41,0 37 | LF:20 38 | LH:14 39 | BRDA:15,1,0,1 40 | BRDA:15,1,1,0 41 | BRF:2 42 | BRH:1 43 | end_of_record 44 | TN: 45 | SF:/Users/JerryC/Workspace/jc-repository/Memeye/src/lib/Collector.js 46 | FN:10,Collector 47 | FN:16,(anonymous_2) 48 | FN:20,(anonymous_3) 49 | FNF:3 50 | FNH:3 51 | FNDA:2,Collector 52 | FNDA:1,(anonymous_2) 53 | FNDA:3,(anonymous_3) 54 | DA:6,1 55 | DA:7,1 56 | DA:8,1 57 | DA:10,1 58 | DA:11,2 59 | DA:12,2 60 | DA:13,2 61 | DA:16,1 62 | DA:17,1 63 | DA:20,1 64 | DA:21,3 65 | DA:23,3 66 | DA:24,1 67 | DA:25,1 68 | DA:28,2 69 | DA:31,1 70 | DA:32,1 71 | DA:36,1 72 | DA:41,1 73 | DA:57,1 74 | DA:58,1 75 | DA:61,1 76 | DA:62,1 77 | DA:68,1 78 | LF:24 79 | LH:24 80 | BRDA:23,1,0,1 81 | BRDA:23,1,1,2 82 | BRDA:57,2,0,1 83 | BRDA:57,2,1,0 84 | BRDA:61,3,0,1 85 | BRDA:61,3,1,0 86 | BRF:6 87 | BRH:4 88 | end_of_record 89 | TN: 90 | SF:/Users/JerryC/Workspace/jc-repository/Memeye/src/lib/Logger.js 91 | FN:19,Logger 92 | FN:25,(anonymous_2) 93 | FN:31,(anonymous_3) 94 | FN:35,(anonymous_4) 95 | FN:39,(anonymous_5) 96 | FN:43,(anonymous_6) 97 | FNF:6 98 | FNH:4 99 | FNDA:1,Logger 100 | FNDA:2,(anonymous_2) 101 | FNDA:0,(anonymous_3) 102 | FNDA:0,(anonymous_4) 103 | FNDA:2,(anonymous_5) 104 | FNDA:1,(anonymous_6) 105 | DA:1,1 106 | DA:3,1 107 | DA:10,1 108 | DA:17,1 109 | DA:19,1 110 | DA:20,1 111 | DA:21,1 112 | DA:22,1 113 | DA:25,1 114 | DA:26,2 115 | DA:27,2 116 | DA:28,2 117 | DA:31,1 118 | DA:32,0 119 | DA:35,1 120 | DA:36,0 121 | DA:39,1 122 | DA:40,2 123 | DA:43,1 124 | DA:44,1 125 | DA:47,1 126 | LF:21 127 | LH:19 128 | BRDA:26,1,0,0 129 | BRDA:26,1,1,2 130 | BRDA:26,2,0,2 131 | BRDA:26,2,1,1 132 | BRDA:27,3,0,1 133 | BRDA:27,3,1,1 134 | BRDA:32,4,0,0 135 | BRDA:32,4,1,0 136 | BRDA:36,5,0,0 137 | BRDA:36,5,1,0 138 | BRDA:40,6,0,2 139 | BRDA:40,6,1,0 140 | BRDA:44,7,0,1 141 | BRDA:44,7,1,0 142 | BRF:14 143 | BRH:7 144 | end_of_record 145 | TN: 146 | SF:/Users/JerryC/Workspace/jc-repository/Memeye/src/lib/Indicators.js 147 | FN:11,Indicators 148 | FN:26,setWatcher 149 | FN:34,(anonymous_3) 150 | FN:38,(anonymous_4) 151 | FNF:4 152 | FNH:4 153 | FNDA:1,Indicators 154 | FNDA:2,setWatcher 155 | FNDA:2,(anonymous_3) 156 | FNDA:1,(anonymous_4) 157 | DA:7,1 158 | DA:8,1 159 | DA:11,1 160 | DA:12,1 161 | DA:15,1 162 | DA:16,1 163 | DA:19,1 164 | DA:23,1 165 | DA:26,1 166 | DA:27,2 167 | DA:28,2 168 | DA:31,2 169 | DA:32,2 170 | DA:33,2 171 | DA:35,2 172 | DA:36,2 173 | DA:39,1 174 | DA:45,2 175 | DA:48,1 176 | LF:19 177 | LH:19 178 | BRF:0 179 | BRH:0 180 | end_of_record 181 | -------------------------------------------------------------------------------- /example/small-example.js: -------------------------------------------------------------------------------- 1 | const memeye = require('../'); 2 | 3 | memeye({log: 'debug'}); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src/index.js'); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "memeye", 3 | "version": "1.0.2", 4 | "description": "A memory monitor and dashboard for Node.js application on development.", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/JerryC8080/Memeye.git" 9 | }, 10 | "scripts": { 11 | "test": "node_modules/.bin/istanbul cover node_modules/.bin/_mocha -- test/bootstarp.js test/unit/*.test.js" 12 | }, 13 | "keywords": [ 14 | "memory", 15 | "monitor", 16 | "dashboard" 17 | ], 18 | "author": "JerryC (huangjerryc@gmail.com)", 19 | "license": "MIT", 20 | "dependencies": { 21 | "colors": "^1.1.2", 22 | "socket.io": "^1.7.3" 23 | }, 24 | "devDependencies": { 25 | "mocha": "^3.2.0", 26 | "istanbul": "^0.4.5", 27 | "should": "^11.2.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/dashboard/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The dashboard , will calling as process way. 3 | * This file will create an Indicator instance and start a http server which provide WebSocket service. 4 | * Then bind indicator with process IPC channel, to recive massage from parent process. 5 | * And then bind the indicator with socketio, to send message while indicator attrbutes changing. 6 | */ 7 | 8 | const Indicator = require('../lib/Indicators'); 9 | const server = require('./server'); 10 | const logger = require('../lib/Logger.js'); 11 | 12 | const port = process.argv[2] || 23333; 13 | let indicator = new Indicator({ 14 | 15 | // process 16 | rss: 'indicators:rss', 17 | heapTotal: 'indicators:heapTotal', 18 | heapUsed: 'indicators:heapUsed', 19 | 20 | // os 21 | freeMem: 'indicators:freeMem', 22 | totalMem: 'indicators:totalMem', 23 | cpus: 'indicators:cpus', 24 | 25 | // v8 heap space 26 | newSpace: 'indicators:newSpace', 27 | oldSpace: 'indicators:oldSpace', 28 | codeSpace: 'indicators:codeSpace', 29 | mapSpace: 'indicators:mapSpace', 30 | largeObjectSpace: 'indicators:largeObjectSpace', 31 | 32 | }); 33 | 34 | // Bind process IPC channel to indicator 35 | function bindIndicators(indicator) { 36 | // Parent process will send three type of data 37 | const collectorHandler = { 38 | 'process': function (value) { 39 | indicator.rss = value.rss; 40 | indicator.heapTotal = value.heapTotal; 41 | indicator.heapUsed = value.heapUsed; 42 | }, 43 | 'os': function (value) { 44 | indicator.totalMem = value.totalMem; 45 | indicator.freeMem = value.freeMem; 46 | indicator.cpus = value.cpus; 47 | }, 48 | 'v8': function (value) { 49 | indicator.newSpace = value.heapSpace[0]; 50 | indicator.oldSpace = value.heapSpace[1]; 51 | indicator.codeSpace = value.heapSpace[2]; 52 | indicator.mapSpace = value.heapSpace[3]; 53 | indicator.largeObjectSpace = value.heapSpace[4]; 54 | } 55 | } 56 | 57 | process.on('message', (msg) => { 58 | if (Array.isArray(msg)) { 59 | msg.forEach((data) => { 60 | let handler = collectorHandler[data.type]; 61 | if (typeof handler === 'function') collectorHandler[data.type](data.value); 62 | }); 63 | } 64 | }); 65 | } 66 | 67 | // Bind indicators to socket 68 | function bindSocket(io, indicator) { 69 | Object.keys(indicator.watch).forEach((key) => { 70 | let eventName = indicator.watch[key]; 71 | indicator.on(eventName, (msg) => io.emit(eventName, msg)); 72 | }); 73 | } 74 | 75 | logger.info('Binding Indicator with Collector by IPC channel...... '); 76 | bindIndicators(indicator); 77 | 78 | logger.info('Binding Indicator with SocketIO...... '); 79 | bindSocket(server.io, indicator); 80 | 81 | // Run dashboard 82 | server.httpServer.listen(port, function () { 83 | logger.info(`Memeye runing on: http://localhost:${port}`); 84 | }); 85 | 86 | -------------------------------------------------------------------------------- /src/dashboard/server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Server will create a http server which provide a socket.io instance. 3 | * Use io instance, you can send msg to the http client. 4 | */ 5 | 6 | const http = require('http'); 7 | const fs = require('fs'); 8 | const path = require('path'); 9 | const socketio = require('socket.io'); 10 | 11 | const staitcPath = path.join(__dirname, '../../assets'); 12 | 13 | const server = http.createServer(function (req, res) { 14 | let url = req.url; 15 | 16 | switch (url) { 17 | case '/': 18 | res.writeHead(200, { 'Content-Type': 'text/html' }); 19 | getStaticFileStream('/index.html', (err, stream) => { 20 | if (err) { 21 | res.writeHead(404); 22 | return res.end('Not Found!'); 23 | } 24 | stream.pipe(res); 25 | }); 26 | break; 27 | default: 28 | getStaticFileStream(url, (err, stream) => { 29 | if (err) { 30 | res.writeHead(404); 31 | return res.end('Not Found!'); 32 | } 33 | stream.pipe(res); 34 | }); 35 | break; 36 | } 37 | }); 38 | 39 | function getStaticFileStream(url, cb) { 40 | let filePath = path.join(staitcPath, url); 41 | fs.stat(filePath, (err, stats) => { 42 | if (err) return cb(err); 43 | cb(null, fs.createReadStream(filePath)); 44 | }); 45 | } 46 | 47 | const io = socketio(server); 48 | 49 | module.exports = { 50 | io, 51 | httpServer: server, 52 | } 53 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The entry of Memeye. 3 | * This file will create collector for collecting info of this process. 4 | * and run dashboard on new process which has its own memory and v8 instance. 5 | * The dashboard will recive data from collector and dispaly on a local website. 6 | */ 7 | 8 | const path = require('path'); 9 | const child_process = require('child_process'); 10 | const Collector = require('./lib/Collector.js'); 11 | const modulePath = path.join(__dirname, './dashboard'); 12 | const logger = require('./lib/Logger.js'); 13 | 14 | module.exports = function ({ port = 23333, log, frequency } = {}) { 15 | if (log) logger.setLevel(log); 16 | 17 | logger.info('Memeye seting up...... '); 18 | 19 | let dashboardProcess = child_process.fork(modulePath, [port]); 20 | 21 | logger.info('Initializing Collector...... '); 22 | 23 | let collector = new Collector(dashboardProcess, { frequency }); 24 | collector.start(); 25 | 26 | // If Parent process exit, kill child process. 27 | process.on('exit', () => dashboardProcess.kill()); 28 | 29 | // Handle child process's error 30 | dashboardProcess.once('error', (err) => { 31 | 32 | // Kill child process once it occur error. 33 | logger.error(`DashboardProcess occur an error: ${err.toString()}`); 34 | logger.error(`DashboardProcess should be stopping......`); 35 | dashboardProcess.kill(); 36 | }).once('exit', (code, signal) => { 37 | 38 | // Stop collector while child process exit. 39 | logger.error(`DashboardProcess exited by code: ${code}`); 40 | logger.error(`Collector has been stopped.`); 41 | collector.stop(); 42 | }); 43 | } -------------------------------------------------------------------------------- /src/lib/Collector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Collector will collecting info from process、v8、os, 3 | * and send thiese data to dashboard process with IPC channel. 4 | */ 5 | 6 | const v8 = require('v8'); 7 | const os = require('os'); 8 | const logger = require('./Logger.js'); 9 | 10 | function Collector(dashboard, { frequency = 1000 } = {}) { 11 | this.dashboard = dashboard; 12 | this.interval = null; 13 | this.frequency = frequency; 14 | } 15 | 16 | Collector.prototype.stop = function () { 17 | clearInterval(this.interval); 18 | } 19 | 20 | Collector.prototype.start = function () { 21 | let that = this; 22 | 23 | if (that.interval) { 24 | console.error('Collector has been started'); 25 | return; 26 | } 27 | 28 | that.interval = setInterval(() => { 29 | 30 | // collecing data 31 | let processStat = process.memoryUsage(); 32 | let v8Stat = { 33 | heap: v8.getHeapStatistics(), 34 | heapSpace: v8.getHeapSpaceStatistics(), 35 | } 36 | let osStat = { 37 | freeMem: os.freemem(), 38 | totalMem: os.totalmem(), 39 | cpus: os.cpus(), 40 | } 41 | let data = [ 42 | { 43 | type: 'process', 44 | value: processStat, 45 | }, 46 | { 47 | type: 'v8', 48 | value: v8Stat, 49 | }, 50 | { 51 | type: 'os', 52 | value: osStat 53 | } 54 | ] 55 | 56 | // Sending data to dashboard process with IPC channel if dashboard keep on connected. 57 | if (this.dashboard.connected) { 58 | that.dashboard.send(data); 59 | } 60 | 61 | if (logger.level >= logger.levelMap['debug']) { 62 | logger.debug(`Collector heartbeat, data collected. -- size: ${(new Buffer(JSON.stringify(data))).length}, timestamp: ${Date.now()}`); 63 | } 64 | 65 | }, that.frequency); 66 | } 67 | 68 | module.exports = Collector; 69 | -------------------------------------------------------------------------------- /src/lib/Indicators.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Indicators like a state machine. 3 | * When attribute changed, indicators instance will emit an event. 4 | * You can listen the event then do your logic. 5 | */ 6 | 7 | const events = require('events'); 8 | const util = require('util'); 9 | 10 | // Constructor 11 | function Indicators(indicators) { 12 | events.EventEmitter.call(this); 13 | 14 | // Define watch object. 15 | this.watch = {}; 16 | Object.assign(this.watch, indicators); 17 | 18 | // Set watcher 19 | this.setWatcher.call(this, indicators); 20 | } 21 | 22 | // Inherit EventEmitter Class 23 | util.inherits(Indicators, events.EventEmitter); 24 | 25 | // Set watcher, it aslo support dynamic adding. 26 | Indicators.prototype.setWatcher = function setWatcher(indicators) { 27 | let propDefine = {}; 28 | let that = this; 29 | 30 | // Define setter/getter for indicators 31 | Object.keys(indicators).forEach((key) => { 32 | that.watch[key] = indicators[key]; 33 | propDefine[key] = { 34 | set: function (value) { 35 | this.emit(indicators[key], value); 36 | this['_' + key] = value; 37 | }, 38 | get: function () { 39 | return this['_' + key]; 40 | }, 41 | enumerable: true, 42 | } 43 | }); 44 | 45 | Object.defineProperties(that, propDefine); 46 | } 47 | 48 | module.exports = Indicators; 49 | -------------------------------------------------------------------------------- /src/lib/Logger.js: -------------------------------------------------------------------------------- 1 | const colors = require('colors/safe'); 2 | 3 | colors.setTheme({ 4 | info: 'green', 5 | warn: 'yellow', 6 | debug: 'blue', 7 | error: 'red' 8 | }); 9 | 10 | const levelMap = { 11 | 'error': 0, 12 | 'warn': 1, 13 | 'info': 2, 14 | 'debug': 3, 15 | } 16 | 17 | Object.freeze(levelMap); 18 | 19 | function Logger(level = 2, prefix = '') { 20 | this.setLevel.call(this, level); 21 | this.prefix = prefix; 22 | this.levelMap = levelMap; 23 | } 24 | 25 | Logger.prototype.setLevel = function (level) { 26 | if (typeof level !== 'string' && typeof level !== 'number') level = 2; 27 | if (typeof level === 'string') level = levelMap[level]; 28 | return this.level = level; 29 | } 30 | 31 | Logger.prototype.error = function (msg) { 32 | if (this.level >= levelMap['error']) console.log(this.prefix + colors['error'](msg)) 33 | } 34 | 35 | Logger.prototype.warn = function (msg) { 36 | if (this.level >= levelMap['warn']) console.log(this.prefix + colors['warn'](msg)) 37 | } 38 | 39 | Logger.prototype.info = function (msg) { 40 | if (this.level >= levelMap['info']) console.log(this.prefix + colors['info'](msg)) 41 | } 42 | 43 | Logger.prototype.debug = function (msg) { 44 | if (this.level >= levelMap['debug']) console.log(this.prefix + colors['debug'](msg)) 45 | } 46 | 47 | module.exports = new Logger(2, '[Memeye] '); -------------------------------------------------------------------------------- /test/bootstarp.js: -------------------------------------------------------------------------------- 1 | const memeye = require('../'); 2 | 3 | before(() => { 4 | memeye({log: 'debug'}); 5 | }); -------------------------------------------------------------------------------- /test/unit/Collector.test.js: -------------------------------------------------------------------------------- 1 | const Collector = require('../../src/lib/Collector.js'); 2 | const should = require('should'); 3 | const events = require('events'); 4 | const v8 = require('v8'); 5 | const os = require('os'); 6 | 7 | describe('#Collector', () => { 8 | let frequency = 50; 9 | let dashboard = new events.EventEmitter(); 10 | dashboard.send = (val) => { dashboard.emit('send', val) } 11 | dashboard.connected = true; 12 | 13 | let collector = new Collector(dashboard, { frequency }); 14 | 15 | it('start a collector', (done) => { 16 | let callDone = false; 17 | let indicatorsStructure = { 18 | process: process.memoryUsage(), 19 | v8: { 20 | heap: v8.getHeapStatistics(), 21 | heapSpace: v8.getHeapSpaceStatistics(), 22 | }, 23 | os: { 24 | freeMem: os.freemem(), 25 | totalMem: os.totalmem(), 26 | cpus: os.cpus(), 27 | } 28 | } 29 | 30 | // mutil call start() should be nothing affect; 31 | collector.start(); 32 | collector.start(); 33 | 34 | dashboard.on('send', (val) => { 35 | should(val).is.Array(); 36 | val.forEach((item) => { 37 | let indicators = indicatorsStructure[item.type]; 38 | should(item.value).has.properties(Object.keys(indicators)); 39 | }); 40 | if (!callDone) { 41 | callDone = true; 42 | done(); 43 | } 44 | }); 45 | }); 46 | 47 | it('stop a collector', (done) => { 48 | let callDone = false; 49 | 50 | collector.stop(); 51 | dashboard.on('send', (val) => { 52 | if (!callDone) { 53 | callDone = true; 54 | done('collector have not stop'); 55 | } 56 | }); 57 | 58 | setTimeout(function () { 59 | done(); 60 | }, frequency * 3); 61 | }) 62 | }); -------------------------------------------------------------------------------- /test/unit/Indicators.test.js: -------------------------------------------------------------------------------- 1 | const Indicators = require('../../src/lib/Indicators.js'); 2 | const should = require('should'); 3 | 4 | describe('#Indicators', () => { 5 | let watcher = { key1: 'hi' }; 6 | let watcherAfter = { key2: 'hello' }; 7 | let indicators; 8 | 9 | it('Add watcher while creating indicators', (done) => { 10 | indicators = new Indicators(watcher); 11 | should(indicators.watch).have.properties(watcher); 12 | 13 | indicators.on(indicators.watch.key1, (val) => { 14 | should(val).equal('hello'); 15 | done(); 16 | }); 17 | 18 | indicators.key1 = 'hello'; 19 | 20 | should(indicators.key1).equal('hello'); 21 | }); 22 | 23 | it('Add watcher after indicators created ', (done) => { 24 | indicators.setWatcher(watcherAfter); 25 | should(indicators.watch).have.properties(Object.assign(watcher, watcherAfter)); 26 | 27 | indicators.on(indicators.watch.key2, (val) => { 28 | should(val).equal('hello'); 29 | done(); 30 | }); 31 | 32 | indicators.key2 = 'hello'; 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | abbrev@1, abbrev@1.0.x: 6 | version "1.0.9" 7 | resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" 8 | 9 | accepts@1.3.3: 10 | version "1.3.3" 11 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" 12 | dependencies: 13 | mime-types "~2.1.11" 14 | negotiator "0.6.1" 15 | 16 | after@0.8.2: 17 | version "0.8.2" 18 | resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" 19 | 20 | align-text@^0.1.1, align-text@^0.1.3: 21 | version "0.1.4" 22 | resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" 23 | dependencies: 24 | kind-of "^3.0.2" 25 | longest "^1.0.1" 26 | repeat-string "^1.5.2" 27 | 28 | amdefine@>=0.0.4: 29 | version "1.0.1" 30 | resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" 31 | 32 | argparse@^1.0.7: 33 | version "1.0.9" 34 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" 35 | dependencies: 36 | sprintf-js "~1.0.2" 37 | 38 | arraybuffer.slice@0.0.6: 39 | version "0.0.6" 40 | resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca" 41 | 42 | async@1.x, async@^1.4.0: 43 | version "1.5.2" 44 | resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" 45 | 46 | backo2@1.0.2: 47 | version "1.0.2" 48 | resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" 49 | 50 | balanced-match@^0.4.1: 51 | version "0.4.2" 52 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" 53 | 54 | base64-arraybuffer@0.1.5: 55 | version "0.1.5" 56 | resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" 57 | 58 | base64id@1.0.0: 59 | version "1.0.0" 60 | resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6" 61 | 62 | better-assert@~1.0.0: 63 | version "1.0.2" 64 | resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" 65 | dependencies: 66 | callsite "1.0.0" 67 | 68 | blob@0.0.4: 69 | version "0.0.4" 70 | resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921" 71 | 72 | brace-expansion@^1.0.0: 73 | version "1.1.6" 74 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" 75 | dependencies: 76 | balanced-match "^0.4.1" 77 | concat-map "0.0.1" 78 | 79 | browser-stdout@1.3.0: 80 | version "1.3.0" 81 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" 82 | 83 | callsite@1.0.0: 84 | version "1.0.0" 85 | resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" 86 | 87 | camelcase@^1.0.2: 88 | version "1.2.1" 89 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" 90 | 91 | center-align@^0.1.1: 92 | version "0.1.3" 93 | resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" 94 | dependencies: 95 | align-text "^0.1.3" 96 | lazy-cache "^1.0.3" 97 | 98 | cliui@^2.1.0: 99 | version "2.1.0" 100 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" 101 | dependencies: 102 | center-align "^0.1.1" 103 | right-align "^0.1.1" 104 | wordwrap "0.0.2" 105 | 106 | commander@2.9.0: 107 | version "2.9.0" 108 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" 109 | dependencies: 110 | graceful-readlink ">= 1.0.0" 111 | 112 | component-bind@1.0.0: 113 | version "1.0.0" 114 | resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" 115 | 116 | component-emitter@1.1.2: 117 | version "1.1.2" 118 | resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.1.2.tgz#296594f2753daa63996d2af08d15a95116c9aec3" 119 | 120 | component-emitter@1.2.1: 121 | version "1.2.1" 122 | resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" 123 | 124 | component-inherit@0.0.3: 125 | version "0.0.3" 126 | resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" 127 | 128 | concat-map@0.0.1: 129 | version "0.0.1" 130 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 131 | 132 | cookie@0.3.1: 133 | version "0.3.1" 134 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" 135 | 136 | debug@2.2.0: 137 | version "2.2.0" 138 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" 139 | dependencies: 140 | ms "0.7.1" 141 | 142 | debug@2.3.3: 143 | version "2.3.3" 144 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.3.3.tgz#40c453e67e6e13c901ddec317af8986cda9eff8c" 145 | dependencies: 146 | ms "0.7.2" 147 | 148 | decamelize@^1.0.0: 149 | version "1.2.0" 150 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 151 | 152 | deep-is@~0.1.3: 153 | version "0.1.3" 154 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 155 | 156 | diff@1.4.0: 157 | version "1.4.0" 158 | resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" 159 | 160 | engine.io-client@1.8.3: 161 | version "1.8.3" 162 | resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.3.tgz#1798ed93451246453d4c6f635d7a201fe940d5ab" 163 | dependencies: 164 | component-emitter "1.2.1" 165 | component-inherit "0.0.3" 166 | debug "2.3.3" 167 | engine.io-parser "1.3.2" 168 | has-cors "1.1.0" 169 | indexof "0.0.1" 170 | parsejson "0.0.3" 171 | parseqs "0.0.5" 172 | parseuri "0.0.5" 173 | ws "1.1.2" 174 | xmlhttprequest-ssl "1.5.3" 175 | yeast "0.1.2" 176 | 177 | engine.io-parser@1.3.2: 178 | version "1.3.2" 179 | resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-1.3.2.tgz#937b079f0007d0893ec56d46cb220b8cb435220a" 180 | dependencies: 181 | after "0.8.2" 182 | arraybuffer.slice "0.0.6" 183 | base64-arraybuffer "0.1.5" 184 | blob "0.0.4" 185 | has-binary "0.1.7" 186 | wtf-8 "1.0.0" 187 | 188 | engine.io@1.8.3: 189 | version "1.8.3" 190 | resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.3.tgz#8de7f97895d20d39b85f88eeee777b2bd42b13d4" 191 | dependencies: 192 | accepts "1.3.3" 193 | base64id "1.0.0" 194 | cookie "0.3.1" 195 | debug "2.3.3" 196 | engine.io-parser "1.3.2" 197 | ws "1.1.2" 198 | 199 | escape-string-regexp@1.0.5: 200 | version "1.0.5" 201 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 202 | 203 | escodegen@1.8.x: 204 | version "1.8.1" 205 | resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" 206 | dependencies: 207 | esprima "^2.7.1" 208 | estraverse "^1.9.1" 209 | esutils "^2.0.2" 210 | optionator "^0.8.1" 211 | optionalDependencies: 212 | source-map "~0.2.0" 213 | 214 | esprima@2.7.x, esprima@^2.7.1: 215 | version "2.7.3" 216 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" 217 | 218 | esprima@^3.1.1: 219 | version "3.1.3" 220 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" 221 | 222 | estraverse@^1.9.1: 223 | version "1.9.3" 224 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" 225 | 226 | esutils@^2.0.2: 227 | version "2.0.2" 228 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 229 | 230 | fast-levenshtein@~2.0.4: 231 | version "2.0.6" 232 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 233 | 234 | fs.realpath@^1.0.0: 235 | version "1.0.0" 236 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 237 | 238 | glob@7.0.5: 239 | version "7.0.5" 240 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95" 241 | dependencies: 242 | fs.realpath "^1.0.0" 243 | inflight "^1.0.4" 244 | inherits "2" 245 | minimatch "^3.0.2" 246 | once "^1.3.0" 247 | path-is-absolute "^1.0.0" 248 | 249 | glob@^5.0.15: 250 | version "5.0.15" 251 | resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" 252 | dependencies: 253 | inflight "^1.0.4" 254 | inherits "2" 255 | minimatch "2 || 3" 256 | once "^1.3.0" 257 | path-is-absolute "^1.0.0" 258 | 259 | "graceful-readlink@>= 1.0.0": 260 | version "1.0.1" 261 | resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" 262 | 263 | growl@1.9.2: 264 | version "1.9.2" 265 | resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" 266 | 267 | handlebars@^4.0.1: 268 | version "4.0.6" 269 | resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.6.tgz#2ce4484850537f9c97a8026d5399b935c4ed4ed7" 270 | dependencies: 271 | async "^1.4.0" 272 | optimist "^0.6.1" 273 | source-map "^0.4.4" 274 | optionalDependencies: 275 | uglify-js "^2.6" 276 | 277 | has-binary@0.1.7: 278 | version "0.1.7" 279 | resolved "https://registry.yarnpkg.com/has-binary/-/has-binary-0.1.7.tgz#68e61eb16210c9545a0a5cce06a873912fe1e68c" 280 | dependencies: 281 | isarray "0.0.1" 282 | 283 | has-cors@1.1.0: 284 | version "1.1.0" 285 | resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" 286 | 287 | has-flag@^1.0.0: 288 | version "1.0.0" 289 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" 290 | 291 | indexof@0.0.1: 292 | version "0.0.1" 293 | resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" 294 | 295 | inflight@^1.0.4: 296 | version "1.0.6" 297 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 298 | dependencies: 299 | once "^1.3.0" 300 | wrappy "1" 301 | 302 | inherits@2: 303 | version "2.0.3" 304 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 305 | 306 | is-buffer@^1.0.2: 307 | version "1.1.4" 308 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" 309 | 310 | isarray@0.0.1: 311 | version "0.0.1" 312 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" 313 | 314 | isexe@^1.1.1: 315 | version "1.1.2" 316 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" 317 | 318 | istanbul@^0.4.5: 319 | version "0.4.5" 320 | resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" 321 | dependencies: 322 | abbrev "1.0.x" 323 | async "1.x" 324 | escodegen "1.8.x" 325 | esprima "2.7.x" 326 | glob "^5.0.15" 327 | handlebars "^4.0.1" 328 | js-yaml "3.x" 329 | mkdirp "0.5.x" 330 | nopt "3.x" 331 | once "1.x" 332 | resolve "1.1.x" 333 | supports-color "^3.1.0" 334 | which "^1.1.1" 335 | wordwrap "^1.0.0" 336 | 337 | js-yaml@3.x: 338 | version "3.8.2" 339 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.2.tgz#02d3e2c0f6beab20248d412c352203827d786721" 340 | dependencies: 341 | argparse "^1.0.7" 342 | esprima "^3.1.1" 343 | 344 | json3@3.3.2: 345 | version "3.3.2" 346 | resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" 347 | 348 | kind-of@^3.0.2: 349 | version "3.1.0" 350 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" 351 | dependencies: 352 | is-buffer "^1.0.2" 353 | 354 | lazy-cache@^1.0.3: 355 | version "1.0.4" 356 | resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" 357 | 358 | levn@~0.3.0: 359 | version "0.3.0" 360 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" 361 | dependencies: 362 | prelude-ls "~1.1.2" 363 | type-check "~0.3.2" 364 | 365 | lodash._baseassign@^3.0.0: 366 | version "3.2.0" 367 | resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" 368 | dependencies: 369 | lodash._basecopy "^3.0.0" 370 | lodash.keys "^3.0.0" 371 | 372 | lodash._basecopy@^3.0.0: 373 | version "3.0.1" 374 | resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" 375 | 376 | lodash._basecreate@^3.0.0: 377 | version "3.0.3" 378 | resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" 379 | 380 | lodash._getnative@^3.0.0: 381 | version "3.9.1" 382 | resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" 383 | 384 | lodash._isiterateecall@^3.0.0: 385 | version "3.0.9" 386 | resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" 387 | 388 | lodash.create@3.1.1: 389 | version "3.1.1" 390 | resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" 391 | dependencies: 392 | lodash._baseassign "^3.0.0" 393 | lodash._basecreate "^3.0.0" 394 | lodash._isiterateecall "^3.0.0" 395 | 396 | lodash.isarguments@^3.0.0: 397 | version "3.1.0" 398 | resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 399 | 400 | lodash.isarray@^3.0.0: 401 | version "3.0.4" 402 | resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" 403 | 404 | lodash.keys@^3.0.0: 405 | version "3.1.2" 406 | resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" 407 | dependencies: 408 | lodash._getnative "^3.0.0" 409 | lodash.isarguments "^3.0.0" 410 | lodash.isarray "^3.0.0" 411 | 412 | longest@^1.0.1: 413 | version "1.0.1" 414 | resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" 415 | 416 | mime-db@~1.26.0: 417 | version "1.26.0" 418 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.26.0.tgz#eaffcd0e4fc6935cf8134da246e2e6c35305adff" 419 | 420 | mime-types@~2.1.11: 421 | version "2.1.14" 422 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.14.tgz#f7ef7d97583fcaf3b7d282b6f8b5679dab1e94ee" 423 | dependencies: 424 | mime-db "~1.26.0" 425 | 426 | "minimatch@2 || 3", minimatch@^3.0.2: 427 | version "3.0.3" 428 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" 429 | dependencies: 430 | brace-expansion "^1.0.0" 431 | 432 | minimist@0.0.8, minimist@~0.0.1: 433 | version "0.0.8" 434 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 435 | 436 | mkdirp@0.5.1, mkdirp@0.5.x: 437 | version "0.5.1" 438 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 439 | dependencies: 440 | minimist "0.0.8" 441 | 442 | mocha@^3.2.0: 443 | version "3.2.0" 444 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.2.0.tgz#7dc4f45e5088075171a68896814e6ae9eb7a85e3" 445 | dependencies: 446 | browser-stdout "1.3.0" 447 | commander "2.9.0" 448 | debug "2.2.0" 449 | diff "1.4.0" 450 | escape-string-regexp "1.0.5" 451 | glob "7.0.5" 452 | growl "1.9.2" 453 | json3 "3.3.2" 454 | lodash.create "3.1.1" 455 | mkdirp "0.5.1" 456 | supports-color "3.1.2" 457 | 458 | ms@0.7.1: 459 | version "0.7.1" 460 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" 461 | 462 | ms@0.7.2: 463 | version "0.7.2" 464 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" 465 | 466 | negotiator@0.6.1: 467 | version "0.6.1" 468 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" 469 | 470 | nopt@3.x: 471 | version "3.0.6" 472 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" 473 | dependencies: 474 | abbrev "1" 475 | 476 | object-assign@4.1.0: 477 | version "4.1.0" 478 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" 479 | 480 | object-component@0.0.3: 481 | version "0.0.3" 482 | resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" 483 | 484 | once@1.x, once@^1.3.0: 485 | version "1.4.0" 486 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 487 | dependencies: 488 | wrappy "1" 489 | 490 | optimist@^0.6.1: 491 | version "0.6.1" 492 | resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" 493 | dependencies: 494 | minimist "~0.0.1" 495 | wordwrap "~0.0.2" 496 | 497 | optionator@^0.8.1: 498 | version "0.8.2" 499 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" 500 | dependencies: 501 | deep-is "~0.1.3" 502 | fast-levenshtein "~2.0.4" 503 | levn "~0.3.0" 504 | prelude-ls "~1.1.2" 505 | type-check "~0.3.2" 506 | wordwrap "~1.0.0" 507 | 508 | options@>=0.0.5: 509 | version "0.0.6" 510 | resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" 511 | 512 | parsejson@0.0.3: 513 | version "0.0.3" 514 | resolved "https://registry.yarnpkg.com/parsejson/-/parsejson-0.0.3.tgz#ab7e3759f209ece99437973f7d0f1f64ae0e64ab" 515 | dependencies: 516 | better-assert "~1.0.0" 517 | 518 | parseqs@0.0.5: 519 | version "0.0.5" 520 | resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" 521 | dependencies: 522 | better-assert "~1.0.0" 523 | 524 | parseuri@0.0.5: 525 | version "0.0.5" 526 | resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a" 527 | dependencies: 528 | better-assert "~1.0.0" 529 | 530 | path-is-absolute@^1.0.0: 531 | version "1.0.1" 532 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 533 | 534 | prelude-ls@~1.1.2: 535 | version "1.1.2" 536 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" 537 | 538 | repeat-string@^1.5.2: 539 | version "1.6.1" 540 | resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" 541 | 542 | resolve@1.1.x: 543 | version "1.1.7" 544 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" 545 | 546 | right-align@^0.1.1: 547 | version "0.1.3" 548 | resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" 549 | dependencies: 550 | align-text "^0.1.1" 551 | 552 | should-equal@^1.0.0: 553 | version "1.0.1" 554 | resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-1.0.1.tgz#0b6e9516f2601a9fb0bb2dcc369afa1c7e200af7" 555 | dependencies: 556 | should-type "^1.0.0" 557 | 558 | should-format@^3.0.2: 559 | version "3.0.3" 560 | resolved "https://registry.yarnpkg.com/should-format/-/should-format-3.0.3.tgz#9bfc8f74fa39205c53d38c34d717303e277124f1" 561 | dependencies: 562 | should-type "^1.3.0" 563 | should-type-adaptors "^1.0.1" 564 | 565 | should-type-adaptors@^1.0.1: 566 | version "1.0.1" 567 | resolved "https://registry.yarnpkg.com/should-type-adaptors/-/should-type-adaptors-1.0.1.tgz#efe5553cdf68cff66e5c5f51b712dc351c77beaa" 568 | dependencies: 569 | should-type "^1.3.0" 570 | should-util "^1.0.0" 571 | 572 | should-type@^1.0.0, should-type@^1.3.0, should-type@^1.4.0: 573 | version "1.4.0" 574 | resolved "https://registry.yarnpkg.com/should-type/-/should-type-1.4.0.tgz#0756d8ce846dfd09843a6947719dfa0d4cff5cf3" 575 | 576 | should-util@^1.0.0: 577 | version "1.0.0" 578 | resolved "https://registry.yarnpkg.com/should-util/-/should-util-1.0.0.tgz#c98cda374aa6b190df8ba87c9889c2b4db620063" 579 | 580 | should@^11.2.0: 581 | version "11.2.0" 582 | resolved "https://registry.yarnpkg.com/should/-/should-11.2.0.tgz#7afca3182c234781d786d2278a87805b5ecf0409" 583 | dependencies: 584 | should-equal "^1.0.0" 585 | should-format "^3.0.2" 586 | should-type "^1.4.0" 587 | should-type-adaptors "^1.0.1" 588 | should-util "^1.0.0" 589 | 590 | socket.io-adapter@0.5.0: 591 | version "0.5.0" 592 | resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz#cb6d4bb8bec81e1078b99677f9ced0046066bb8b" 593 | dependencies: 594 | debug "2.3.3" 595 | socket.io-parser "2.3.1" 596 | 597 | socket.io-client@1.7.3: 598 | version "1.7.3" 599 | resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.7.3.tgz#b30e86aa10d5ef3546601c09cde4765e381da377" 600 | dependencies: 601 | backo2 "1.0.2" 602 | component-bind "1.0.0" 603 | component-emitter "1.2.1" 604 | debug "2.3.3" 605 | engine.io-client "1.8.3" 606 | has-binary "0.1.7" 607 | indexof "0.0.1" 608 | object-component "0.0.3" 609 | parseuri "0.0.5" 610 | socket.io-parser "2.3.1" 611 | to-array "0.1.4" 612 | 613 | socket.io-parser@2.3.1: 614 | version "2.3.1" 615 | resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-2.3.1.tgz#dd532025103ce429697326befd64005fcfe5b4a0" 616 | dependencies: 617 | component-emitter "1.1.2" 618 | debug "2.2.0" 619 | isarray "0.0.1" 620 | json3 "3.3.2" 621 | 622 | socket.io@^1.7.3: 623 | version "1.7.3" 624 | resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.7.3.tgz#b8af9caba00949e568e369f1327ea9be9ea2461b" 625 | dependencies: 626 | debug "2.3.3" 627 | engine.io "1.8.3" 628 | has-binary "0.1.7" 629 | object-assign "4.1.0" 630 | socket.io-adapter "0.5.0" 631 | socket.io-client "1.7.3" 632 | socket.io-parser "2.3.1" 633 | 634 | source-map@^0.4.4: 635 | version "0.4.4" 636 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" 637 | dependencies: 638 | amdefine ">=0.0.4" 639 | 640 | source-map@~0.2.0: 641 | version "0.2.0" 642 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" 643 | dependencies: 644 | amdefine ">=0.0.4" 645 | 646 | source-map@~0.5.1: 647 | version "0.5.6" 648 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" 649 | 650 | sprintf-js@~1.0.2: 651 | version "1.0.3" 652 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 653 | 654 | supports-color@3.1.2, supports-color@^3.1.0: 655 | version "3.1.2" 656 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" 657 | dependencies: 658 | has-flag "^1.0.0" 659 | 660 | to-array@0.1.4: 661 | version "0.1.4" 662 | resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" 663 | 664 | type-check@~0.3.2: 665 | version "0.3.2" 666 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 667 | dependencies: 668 | prelude-ls "~1.1.2" 669 | 670 | uglify-js@^2.6: 671 | version "2.8.7" 672 | resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.7.tgz#e0391911507b6d2e05697a528f1686e90a11b160" 673 | dependencies: 674 | source-map "~0.5.1" 675 | uglify-to-browserify "~1.0.0" 676 | yargs "~3.10.0" 677 | 678 | uglify-to-browserify@~1.0.0: 679 | version "1.0.2" 680 | resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" 681 | 682 | ultron@1.0.x: 683 | version "1.0.2" 684 | resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" 685 | 686 | which@^1.1.1: 687 | version "1.2.12" 688 | resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192" 689 | dependencies: 690 | isexe "^1.1.1" 691 | 692 | window-size@0.1.0: 693 | version "0.1.0" 694 | resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" 695 | 696 | wordwrap@0.0.2: 697 | version "0.0.2" 698 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" 699 | 700 | wordwrap@^1.0.0, wordwrap@~1.0.0: 701 | version "1.0.0" 702 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" 703 | 704 | wordwrap@~0.0.2: 705 | version "0.0.3" 706 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" 707 | 708 | wrappy@1: 709 | version "1.0.2" 710 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 711 | 712 | ws@1.1.2: 713 | version "1.1.2" 714 | resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f" 715 | dependencies: 716 | options ">=0.0.5" 717 | ultron "1.0.x" 718 | 719 | wtf-8@1.0.0: 720 | version "1.0.0" 721 | resolved "https://registry.yarnpkg.com/wtf-8/-/wtf-8-1.0.0.tgz#392d8ba2d0f1c34d1ee2d630f15d0efb68e1048a" 722 | 723 | xmlhttprequest-ssl@1.5.3: 724 | version "1.5.3" 725 | resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e4070d99f7b49de3528992d" 726 | 727 | yargs@~3.10.0: 728 | version "3.10.0" 729 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" 730 | dependencies: 731 | camelcase "^1.0.2" 732 | cliui "^2.1.0" 733 | decamelize "^1.0.0" 734 | window-size "0.1.0" 735 | 736 | yeast@0.1.2: 737 | version "0.1.2" 738 | resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" 739 | --------------------------------------------------------------------------------