Repository: antonmedv/spark Branch: master Commit: 7cf8fc551f14 Files: 13 Total size: 51.6 KB Directory structure: gitextract_teum0vry/ ├── .gitignore ├── README.md ├── api.js ├── db.js ├── index.js ├── package.json ├── page.js ├── queue.js ├── render.js ├── style.css ├── svg.js ├── sync.js └── ttl.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /node_modules/ .env ================================================ FILE: README.md ================================================ # ⚡️ Spark Go to [stars.medv.io](https://stars.medv.io) Spark is a generator of pretty little graphs called sparklines, which shows GitHub stars velocity of a repo. ## Development Clone repo, create _.env_ file with [personal access token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/): ``` ACCESS_TOKEN=... ``` Install packages, start server: ``` npm install node index.js ``` ## Rate limit Spark can't eagerly load all stars of a repo, only 100 stargazers per requset allowed, and GitHub API v4 has rate limit of 5000 points per hour. Spark has special endpoint for monitoring remaining rate limit, queue size and processing svg: [stars.medv.io/status](https://stars.medv.io/status) ## Related - https://github.com/qoomon/starline ## License MIT ================================================ FILE: api.js ================================================ const delay = require('delay') const r2 = require('r2') const token = process.env.ACCESS_TOKEN const headers = { Authorization: `bearer ${token}` } async function fetch(query, variables) { let data do { try { const response = await r2.post('https://api.github.com/graphql', { headers, json: {query, variables} }).response const json = await response.json() if (json.data) { data = json.data } else { if (json.message) { process.stderr.write(json.message + '\n') } else { process.stderr.write(JSON.stringify(json) + '\n') } if (response.headers.has('Retry-After')) { const retryAfter = parseInt(response.headers.get('Retry-After')) await delay(retryAfter * 1000) } else { await delay(60 * 1000) } } } catch (err) { process.stderr.write(JSON.stringify(json) + '\n') await delay(5 * 1000) } } while (!data) return data } module.exports = {fetch} ================================================ FILE: db.js ================================================ module.exports = { "freeCodeCamp/freeCodeCamp": 291967, "twbs/bootstrap": 123665, "EbookFoundation/free-programming-books": 103899, "tensorflow/tensorflow": 96276, "facebook/react": 93486, "vuejs/vue": 90762, "sindresorhus/awesome": 82886, "getify/You-Dont-Know-JS": 79982, "d3/d3": 74789, "airbnb/javascript": 69412, "robbyrussell/oh-my-zsh": 68753, "github/gitignore": 64371, "facebook/react-native": 62561, "jwasham/coding-interview-university": 60616, "electron/electron": 58918, "angular/angular.js": 58302, "torvalds/linux": 57592, "FortAwesome/Font-Awesome": 55827, "daneden/animate.css": 50628, "jquery/jquery": 48660, "moby/moby": 48468, "vinta/awesome-python": 48435, "Microsoft/vscode": 47941, "nodejs/node": 47596, "kamranahmedse/developer-roadmap": 47131, "facebook/create-react-app": 46967, "atom/atom": 44391, "apple/swift": 43374, "laravel/laravel": 42122, "mrdoob/three.js": 40970, "socketio/socket.io": 40690, "Semantic-Org/Semantic-UI": 40679, "golang/go": 40273, "h5bp/html5-boilerplate": 40215, "reactjs/redux": 39939, "hakimel/reveal.js": 39882, "webpack/webpack": 39860, "axios/axios": 39780, "meteor/meteor": 39593, "rails/rails": 39331, "expressjs/express": 37664, "moment/moment": 36429, "chartjs/Chart.js": 36245, "nodejs/node-v0.x-archive": 35921, "rg3/youtube-dl": 35900, "resume/resume.github.com": 35698, "toddmotto/public-apis": 35543, "angular/angular": 35067, "kubernetes/kubernetes": 35043, "mui-org/material-ui": 34936, "jakubroztocil/httpie": 34896, "nvbn/thefuck": 34760, "pallets/flask": 34711, "jlevy/the-art-of-command-line": 34536, "h5bp/Front-end-Developer-Interview-Questions": 34300, "google/material-design-icons": 34253, "getlantern/lantern": 34117, "jekyll/jekyll": 33940, "ionic-team/ionic": 33858, "nwjs/nw.js": 33529, "impress/impress.js": 33358, "x64dbg/x64dbg": 33232, "django/django": 33192, "Microsoft/TypeScript": 33047, "tensorflow/models": 33001, "mtdvio/every-programmer-should-know": 32793, "ReactiveX/RxJava": 32304, "Dogfalo/materialize": 32236, "iluwatar/java-design-patterns": 32110, "josephmisiti/awesome-machine-learning": 32064, "requests/requests": 31725, "vuejs/awesome-vue": 31362, "ossu/computer-science": 31247, "yarnpkg/yarn": 31173, "NARKOZ/hacker-scripts": 31155, "lodash/lodash": 31045, "justjavac/free-programming-books-zh_CN": 31042, "AFNetworking/AFNetworking": 30922, "necolas/normalize.css": 30623, "bitcoin/bitcoin": 30577, "typicode/json-server": 30345, "GoogleChrome/puppeteer": 30269, "elastic/elasticsearch": 30180, "google/material-design-lite": 29880, "avelino/awesome-go": 29742, "ansible/ansible": 29635, "ReactTraining/react-router": 29565, "wasabeef/awesome-android-ui": 29559, "papers-we-love/papers-we-love": 29543, "gulpjs/gulp": 29198, "adobe/brackets": 28857, "firehol/netdata": 28824, "blueimp/jQuery-File-Upload": 28585, "Homebrew/legacy-homebrew": 28560, "antirez/redis": 28521, "keras-team/keras": 28244, "adam-p/markdown-here": 27980, "rust-lang/rust": 27682, "Alamofire/Alamofire": 27570, "scikit-learn/scikit-learn": 27385, "square/retrofit": 27360, "zurb/foundation-sites": 27211, "jashkenas/backbone": 27104, "babel/babel": 27072, "thedaviddias/Front-End-Checklist": 27038, "ant-design/ant-design": 26940, "apache/incubator-echarts": 26919, "scrapy/scrapy": 26731, "neovim/neovim": 26690, "googlesamples/android-architecture": 26559, "Trinea/android-open-project": 26470, "chrislgarry/Apollo-11": 26319, "creationix/nvm": 26079, "square/okhttp": 26058, "jgthms/bulma": 25902, "ElemeFE/element": 25416, "TryGhost/Ghost": 25352, "google/protobuf": 25225, "ariya/phantomjs": 25207, "donnemartin/system-design-primer": 25206, "vsouza/awesome-ios": 25077, "enaqx/awesome-react": 24971, "gohugoio/hugo": 24884, "tiimgreen/github-cheat-sheet": 24824, "discourse/discourse": 24721, "FreeCodeCampChina/freecodecamp.cn": 24548, "shadowsocks/shadowsocks": 24458, "Unitech/pm2": 24269, "zeit/next.js": 24248, "gogits/gogs": 24192, "nylas/nylas-mail": 23977, "dypsilon/frontend-dev-bookmarks": 23969, "awesomedata/awesome-public-datasets": 23885, "johnpapa/angular-styleguide": 23803, "lukehoban/es6features": 23794, "prakhar1989/awesome-courses": 23768, "caolan/async": 23759, "opencv/opencv": 23757, "BVLC/caffe": 23710, "minimaxir/big-list-of-naughty-strings": 23630, "joshbuchea/HEAD": 23569, "prettier/prettier": 23564, "tastejs/todomvc": 23459, "google/guava": 23457, "spring-projects/spring-boot": 23328, "facebook/immutable-js": 23239, "sahat/hackathon-starter": 23180, "sindresorhus/awesome-nodejs": 23168, "alvarotrigo/fullPage.js": 23108, "karan/Projects": 23076, "serverless/serverless": 22872, "lord/slate": 22839, "jashkenas/underscore": 22699, "storybooks/storybook": 22583, "almasaeed2010/AdminLTE": 22550, "Modernizr/Modernizr": 22390, "Kickball/awesome-selfhosted": 22364, "codepath/android_guides": 22291, "tonsky/FiraCode": 22223, "XX-net/XX-Net": 22205, "select2/select2": 22182, "kdn251/interviews": 22174, "zeit/hyper": 22161, "astaxie/build-web-application-with-golang": 21908, "JetBrains/kotlin": 21862, "certbot/certbot": 21829, "git/git": 21829, "zenorocha/clipboard.js": 21776, "sdmg15/Best-websites-a-programmer-should-visit": 21692, "mozilla/pdf.js": 21674, "PhilJay/MPAndroidChart": 21600, "hexojs/hexo": 21588, "harvesthq/chosen": 21549, "flutter/flutter": 21540, "aymericdamien/TensorFlow-Examples": 21512, "fastlane/fastlane": 21405, "parcel-bundler/parcel": 21376, "Leaflet/Leaflet": 21320, "kenwheeler/slick": 21261, "bayandin/awesome-awesomeness": 21207, "bumptech/glide": 21193, "grafana/grafana": 21171, "shadowsocks/shadowsocks-windows": 21168, "rethinkdb/rethinkdb": 21086, "Hack-with-Github/Awesome-Hacking": 21048, "photonstorm/phaser": 21043, "videojs/video.js": 20869, "gatsbyjs/gatsby": 20781, "JakeWharton/butterknife": 20742, "koajs/koa": 20716, "alex/what-happens-when": 20630, "gitlabhq/gitlabhq": 20595, "kamranahmedse/design-patterns-for-humans": 20494, "spring-projects/spring-framework": 20385, "yangshun/tech-interview-handbook": 20238, "syncthing/syncthing": 20116, "airbnb/lottie-android": 20046, "juliangarnier/anime": 19820, "kelseyhightower/nocode": 19752, "nvie/gitflow": 19707, "rs/SDWebImage": 19705, "k88hudson/git-flight-rules": 19686, "Chalarangelo/30-seconds-of-code": 19611, "open-guides/og-aws": 19555, "Polymer/polymer": 19393, "request/request": 19165, "FezVrasta/bootstrap-material-design": 19159, "square/leakcanary": 19085, "herrbischoff/awesome-macos-command-line": 19010, "Reactive-Extensions/RxJS": 18929, "emberjs/ember.js": 18904, "IanLunn/Hover": 18898, "facebook/pop": 18820, "balderdashy/sails": 18818, "mathiasbynens/dotfiles": 18810, "apache/incubator-superset": 18775, "MaximAbramchuck/awesome-interview-questions": 18650, "ReactiveCocoa/ReactiveCocoa": 18597, "aosabook/500lines": 18588, "ZuzooVn/machine-learning-for-software-engineers": 18559, "Bilibili/ijkplayer": 18550, "plataformatec/devise": 18503, "pure-css/pure": 18488, "huginn/huginn": 18488, "railsware/upterm": 18464, "ripienaar/free-for-dev": 18460, "github/fetch": 18454, "jondot/awesome-react-native": 18453, "ziadoz/awesome-php": 18451, "developit/preact": 18398, "Tencent/weui": 18330, "pixijs/pixi.js": 18305, "t4t5/sweetalert": 18278, "soimort/you-get": 18211, "tipsy/profile-summary-for-github": 18167, "postcss/postcss": 18166, "hammerjs/hammer.js": 18147, "google/web-starter-kit": 18141, "zxing/zxing": 18126, "ryanmcdermott/clean-code-javascript": 18083, "isocpp/CppCoreGuidelines": 18009, "GitbookIO/gitbook": 18001, "apache/incubator-dubbo": 17976, "greenrobot/EventBus": 17933, "jaywcjlove/awesome-mac": 17925, "danielgindi/Charts": 17925, "Prinzhorn/skrollr": 17880, "numbbbbb/the-swift-programming-language-in-chinese": 17866, "coreos/etcd": 17784, "react-boilerplate/react-boilerplate": 17755, "freeCodeCamp/devdocs": 17708, "tesseract-ocr/tesseract": 17684, "bailicangdu/vue2-elm": 17663, "quilljs/quill": 17555, "bevacqua/dragula": 17513, "standard/standard": 17433, "FallibleInc/security-guide-for-developers": 17407, "kriasoft/react-starter-kit": 17390, "RocketChat/Rocket.Chat": 17371, "Blankj/AndroidUtilCode": 17295, "VundleVim/Vundle.vim": 17278, "symfony/symfony": 17277, "BradLarson/GPUImage": 17270, "ajaxorg/ace": 17268, "0xAX/linux-insides": 17253, "elsewhencode/project-guidelines": 17238, "php/php-src": 17209, "racaljk/hosts": 17176, "python/cpython": 17156, "CyC2018/Interview-Notebook": 17075, "exacity/deeplearningbook-chinese": 17075, "verekia/js-stack-from-scratch": 17034, "kahun/awesome-sysadmin": 16983, "SamyPesse/How-to-Make-a-Computer-Operating-System": 16980, "dkhamsing/open-source-ios-apps": 16949, "angular/angular-cli": 16940, "getlantern/forum": 16922, "facebook/jest": 16911, "apache/spark": 16903, "tldr-pages/tldr": 16848, "floodsung/Deep-Learning-Papers-Reading-Roadmap": 16795, "raywenderlich/swift-algorithm-club": 16778, "mholt/caddy": 16736, "SwiftyJSON/SwiftyJSON": 16721, "ethereum/go-ethereum": 16623, "petkaantonov/bluebird": 16611, "mbeaudru/modern-js-cheatsheet": 16544, "usablica/intro.js": 16521, "hashicorp/vagrant": 16519, "webtorrent/webtorrent": 16422, "alibaba/weex": 16417, "pugjs/pug": 16417, "houshanren/hangzhou_house_knowledge": 16415, "gin-gonic/gin": 16402, "akullpp/awesome-java": 16321, "getsentry/sentry": 16297, "SnapKit/Masonry": 16258, "dimsemenov/PhotoSwipe": 16229, "Valloric/YouCompleteMe": 16217, "ftlabs/fastclick": 16201, "futurice/android-best-practices": 16175, "angular/material": 16117, "facebook/flow": 16114, "nostra13/Android-Universal-Image-Loader": 16061, "npm/npm": 16047, "bcit-ci/CodeIgniter": 15985, "nolimits4web/swiper": 15974, "markedjs/marked": 15968, "rstacruz/nprogress": 15950, "knsv/mermaid": 15893, "prometheus/prometheus": 15800, "defunkt/jquery-pjax": 15759, "cheeriojs/cheerio": 15648, "ReactiveX/RxAndroid": 15601, "jcjohnson/neural-style": 15565, "tornadoweb/tornado": 15541, "wg/wrk": 15520, "dhg/Skeleton": 15489, "google/iosched": 15475, "less/less.js": 15463, "styled-components/styled-components": 15432, "dokku/dokku": 15420, "Automattic/mongoose": 15414, "fouber/blog": 15407, "Kong/kong": 15364, "bower/bower": 15306, "segmentio/nightmare": 15297, "square/picasso": 15291, "fzaninotto/Faker": 15285, "facebook/hhvm": 15252, "mochajs/mocha": 15148, "twitter/typeahead.js": 15119, "DefinitelyTyped/DefinitelyTyped": 15091, "postcss/autoprefixer": 15070, "facebook/flux": 15048, "faif/python-patterns": 14964, "parse-community/parse-server": 14954, "jlmakes/scrollreveal": 14936, "ggreer/the_silver_searcher": 14919, "astaxie/beego": 14870, "tobiasahlin/SpinKit": 14775, "grpc/grpc": 14759, "domnikl/DesignPatternsPHP": 14757, "CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers": 14721, "jashkenas/coffeescript": 14676, "syl20bnr/spacemacs": 14666, "VincentGarreau/particles.js": 14657, "angular-ui/bootstrap": 14634, "godotengine/godot": 14623, "puikinsh/gentelella": 14526, "containous/traefik": 14476, "skylot/jadx": 14473, "mobxjs/mobx": 14469, "CodeHubApp/CodeHub": 14459, "kripken/emscripten": 14443, "gothinkster/realworld": 14439, "reddit-archive/reddit": 14438, "julianshapiro/velocity": 14436, "jdg/MBProgressHUD": 14414, "terryum/awesome-deep-learning-papers": 14393, "codemirror/CodeMirror": 14371, "facebook/fresco": 14366, "twbs/ratchet": 14311, "matteocrippa/awesome-swift": 14292, "jiahaog/nativefier": 14284, "remy/nodemon": 14258, "Microsoft/CNTK": 14225, "ruby/ruby": 14198, "sindresorhus/awesome-electron": 14175, "kriskowal/q": 14127, "iview/iview": 14093, "kennethreitz/python-guide": 14081, "zeeshanu/learn-regex": 14055, "composer/composer": 14044, "pytorch/pytorch": 14014, "junegunn/fzf": 13992, "hubotio/hubot": 13980, "StreisandEffect/streisand": 13957, "designmodo/Flat-UI": 13940, "shadowsocks/shadowsocks-android": 13922, "sequelize/sequelize": 13911, "drone/drone": 13901, "vuejs/vuex": 13887, "angular/material2": 13878, "sorrycc/awesome-javascript": 13862, "bbatsov/ruby-style-guide": 13828, "pandas-dev/pandas": 13824, "angular-ui/ui-router": 13815, "rwaldron/idiomatic.js": 13809, "google/styleguide": 13806, "Marak/faker.js": 13790, "cmderdev/cmder": 13784, "dotnet/corefx": 13758, "home-assistant/home-assistant": 13735, "limetext/lime": 13722, "libgdx/libgdx": 13677, "madrobby/zepto": 13670, "apache/incubator-mxnet": 13670, "julycoding/The-Art-Of-Programming-By-July": 13651, "enyo/dropzone": 13623, "wangshub/wechat_jump_game": 13600, "markerikson/react-redux-links": 13569, "facebookresearch/fastText": 13565, "netty/netty": 13550, "avajs/ava": 13535, "HubSpot/pace": 13522, "mongodb/mongo": 13499, "MostlyAdequate/mostly-adequate-guide": 13457, "jasmine/jasmine": 13442, "facebookresearch/Detectron": 13434, "brillout/awesome-react-components": 13430, "justjavac/awesome-wechat-weapp": 13410, "yangshun/front-end-interview-handbook": 13401, "airbnb/enzyme": 13394, "vapor/vapor": 13359, "naptha/tesseract.js": 13325, "google/leveldb": 13322, "airbnb/lottie-ios": 13297, "lkzhao/Hero": 13266, "i0natan/nodebestpractices": 13264, "angular/angular-seed": 13261, "ruanyf/jstraining": 13244, "Netflix/Hystrix": 13241, "alibaba/fastjson": 13201, "legomushroom/mojs": 13189, "wycats/handlebars.js": 13184, "NativeScript/NativeScript": 13171, "cockroachdb/cockroach": 13157, "xitu/gold-miner": 13138, "nefe/You-Dont-Need-jQuery": 13135, "GoogleChrome/lighthouse": 13102, "inconshreveable/ngrok": 13100, "influxdata/influxdb": 13097, "jaredhanson/passport": 13096, "RubaXa/Sortable": 13081, "wsargent/docker-cheat-sheet": 13045, "desandro/masonry": 13041, "tootsuite/mastodon": 13020, "afollestad/material-dialogs": 13009, "drduh/macOS-Security-and-Privacy-Guide": 13002, "CymChad/BaseRecyclerViewAdapterHelper": 12990, "ReactiveX/RxSwift": 12986, "riot/riot": 12928, "fxsjy/jieba": 12874, "chrisbanes/PhotoView": 12858, "kilimchoi/engineering-blogs": 12846, "amix/vimrc": 12844, "feathericons/feather": 12836, "PerfectlySoft/Perfect": 12826, "Popmotion/popmotion": 12819, "redux-saga/redux-saga": 12818, "chenglou/react-motion": 12810, "wagerfield/parallax": 12807, "fffaraz/awesome-cpp": 12803, "etsy/statsd": 12791, "ipader/SwiftGuide": 12774, "react-bootstrap/react-bootstrap": 12758, "caskroom/homebrew-cask": 12741, "interagent/http-api-design": 12736, "braydie/HowToBeAProgrammer": 12730, "paularmstrong/normalizr": 12719, "elixir-lang/elixir": 12719, "ruanyf/react-demos": 12703, "git-tips/tips": 12703, "donnemartin/interactive-coding-challenges": 12672, "ageitgey/face_recognition": 12669, "pingcap/tidb": 12649, "geekcompany/ResumeSample": 12646, "ipython/ipython": 12641, "wekan/wekan": 12631, "twbs/bootstrap-sass": 12625, "github/hub": 12623, "facebook/draft-js": 12609, "visionmedia/superagent": 12586, "ionic-team/ionicons": 12570, "danielmiessler/SecLists": 12551, "sentsin/layui": 12541, "adobe-fonts/source-code-pro": 12512, "infernojs/inferno": 12472, "facebookarchive/AsyncDisplayKit": 12469, "ipfs/ipfs": 12469, "keystonejs/keystone": 12466, "SnapKit/SnapKit": 12454, "graphcool/chromeless": 12427, "docker/compose": 12406, "altercation/solarized": 12374, "ccgus/fmdb": 12374, "rollup/rollup": 12357, "vim/vim": 12310, "guzzle/guzzle": 12281, "FormidableLabs/webpack-dashboard": 12275, "google/gson": 12270, "donnemartin/data-science-ipython-notebooks": 12258, "ruanyf/es6tutorial": 12223, "mattermost/mattermost-server": 12216, "google/xi-editor": 12194, "uikit/uikit": 12189, "JacksonTian/fks": 12174, "gorhill/uBlock": 12131, "framework7io/framework7": 12127, "alsotang/node-lessons": 12124, "fbsamples/f8app": 12123, "spf13/spf13-vim": 12122, "hyperapp/hyperapp": 12105, "yabwe/medium-editor": 12100, "MengTo/Spring": 12096, "node-inspector/node-inspector": 12084, "alebcay/awesome-shell": 12080, "reactjs/react-redux": 12066, "facebook/osquery": 12056, "realm/realm-cocoa": 12045, "isagalaev/highlight.js": 12017, "buunguyen/octotree": 12012, "rapid7/metasploit-framework": 11982, "nsqio/nsq": 11964, "airyland/vux": 11941, "browserify/browserify": 11904, "janl/mustache.js": 11878, "hashicorp/consul": 11868, "yiisoft/yii2": 11843, "Homebrew/brew": 11839, "ibireme/YYKit": 11822, "cocos2d/cocos2d-x": 11798, "CoderMJLee/MJRefresh": 11794, "diaspora/diaspora": 11791, "Bilibili/flv.js": 11774, "gruntjs/grunt": 11771, "laravel/framework": 11771, "hemanth/functional-programming-jargon": 11759, "ReactiveX/rxjs": 11759, "hashicorp/terraform": 11728, "mysqljs/mysql": 11726, "ramda/ramda": 11688, "localForage/localForage": 11680, "iissnan/hexo-theme-next": 11662, "Tencent/tinker": 11650, "oxford-cs-deepnlp-2017/lectures": 11636, "facebook/prepack": 11624, "HelloZeroNet/ZeroNet": 11612, "phoenixframework/phoenix": 11606, "amazeui/amazeui": 11596, "bolshchikov/js-must-watch": 11590, "textmate/textmate": 11579, "dmlc/xgboost": 11563, "Developer-Y/cs-video-courses": 11548, "lukasz-madon/awesome-remote-job": 11543, "substack/stream-handbook": 11521, "servo/servo": 11512, "benweet/stackedit": 11510, "localstack/localstack": 11506, "reactnativecn/react-native-guide": 11492, "chriskempson/tomorrow-theme": 11484, "lhc70000/iina": 11479, "requirejs/requirejs": 11477, "powerline/fonts": 11473, "android10/Android-CleanArchitecture": 11468, "nuxt/nuxt.js": 11466, "onevcat/Kingfisher": 11444, "ampproject/amphtml": 11438, "niklasvh/html2canvas": 11435, "openai/gym": 11432, "airbnb/react-sketchapp": 11373, "lgvalle/Material-Animations": 11352, "denysdovhan/wtfjs": 11347, "Carthage/Carthage": 11344, "jgm/pandoc": 11340, "fatedier/frp": 11319, "tj/git-extras": 11306, "jtoy/awesome-tensorflow": 11294, "scottjehl/Respond": 11282, "hackiftekhar/IQKeyboardManager": 11271, "sass/sass": 11262, "swagger-api/swagger-ui": 11231, "cayleygraph/cayley": 11219, "PanJiaChen/vue-element-admin": 11217, "JedWatson/react-select": 11211, "apache/predictionio": 11200, "keon/algorithms": 11188, "stedolan/jq": 11177, "adobe-webplatform/Snap.svg": 11154, "marcuswestin/store.js": 11152, "google/flexbox-layout": 11150, "h5bp/Effeckt.css": 11147, "google/deepdream": 11145, "sqlmapproject/sqlmap": 11142, "scwang90/SmartRefreshLayout": 11129, "Flipboard/react-canvas": 11124, "erikras/react-redux-universal-hot-example": 11121, "Shopify/dashing": 11111, "eslint/eslint": 11103, "google/guetzli": 11098, "akveo/ngx-admin": 11082, "reactjs/reselect": 11064, "linnovate/mean": 11039, "binux/pyspider": 11038, "abhishekbanthia/Public-APIs": 11034, "react-navigation/react-navigation": 11023, "jessesquires/JSQMessagesViewController": 11018, "foreverjs/forever": 11012, "viccalexander/Chameleon": 11009, "yaronn/blessed-contrib": 10993, "shadowsocks/ShadowsocksX-NG": 10989, "vuejs/vue-cli": 10985, "philipwalton/solved-by-flexbox": 10985, "airbnb/lottie-web": 10979, "shieldfy/API-Security-Checklist": 10957, "ty4z2008/Qix": 10954, "Mantle/Mantle": 10952, "jfeinstein10/SlidingMenu": 10943, "minio/minio": 10937, "sampotts/plyr": 10934, "cubiq/iscroll": 10930, "alibaba/druid": 10907, "JohnCoates/Aerial": 10904, "frappe/charts": 10895, "nickbutcher/plaid": 10885, "nagadomi/waifu2x": 10875, "jmcunningham/AngularJS-Learning": 10870, "PHPMailer/PHPMailer": 10869, "selectize/selectize.js": 10868, "strongloop/loopback": 10858, "SVProgressHUD/SVProgressHUD": 10847, "facebook/relay": 10819, "tj/commander.js": 10815, "WordPress/WordPress": 10797, "source-foundry/Hack": 10789, "sindresorhus/quick-look-plugins": 10769, "loverajoel/jstips": 10740, "uxsolutions/bootstrap-datepicker": 10730, "pyenv/pyenv": 10728, "hzlzh/Best-App": 10700, "leereilly/games": 10683, "Kotlin/anko": 10656, "JuliaLang/julia": 10645, "vim-airline/vim-airline": 10645, "magicalpanda/MagicalRecord": 10642, "Shopify/draggable": 10641, "koalaman/shellcheck": 10630, "gionkunz/chartist-js": 10619, "MrRio/jsPDF": 10613, "begriffs/postgrest": 10612, "luanfujun/deep-photo-styletransfer": 10574, "react-native-training/react-native-elements": 10566, "marcuswestin/WebViewJavascriptBridge": 10547, "nswbmw/N-blog": 10546, "crystal-lang/crystal": 10524, "junegunn/vim-plug": 10517, "ocornut/imgui": 10504, "aurelia/framework": 10482, "google/fonts": 10481, "ElemeFE/mint-ui": 10473, "Tencent/mars": 10461, "mitmproxy/mitmproxy": 10452, "FFmpeg/FFmpeg": 10438, "rbenv/rbenv": 10433, "facebook/folly": 10431, "nicklockwood/iCarousel": 10402, "transloadit/uppy": 10398, "VerbalExpressions/JSVerbalExpressions": 10396, "equinusocio/material-theme": 10395, "chaozh/awesome-blockchain-cn": 10391, "grab/front-end-guide": 10389, "jquery/jquery-ui": 10388, "xgrommx/awesome-redux": 10386, "pouchdb/pouchdb": 10386, "jwagner/smartcrop.js": 10385, "CocoaPods/CocoaPods": 10384, "resin-io/etcher": 10374, "phanan/htaccess": 10372, "DrkSephy/es6-cheatsheet": 10370, "bang590/JSPatch": 10346, "playframework/playframework": 10340, "capistrano/capistrano": 10323, "facebook/rocksdb": 10322, "froala/design-blocks": 10249, "RestKit/RestKit": 10246, "AllThingsSmitty/css-protips": 10242, "encode/django-rest-framework": 10236, "jeromeetienne/AR.js": 10230, "androidannotations/androidannotations": 10206, "loopj/android-async-http": 10201, "pypa/pipenv": 10196, "browserstate/history.js": 10185, "SeleniumHQ/selenium": 10177, "ptmt/react-native-macos": 10162, "thomaspark/bootswatch": 10158, "CocoaLumberjack/CocoaLumberjack": 10154, "dimsemenov/Magnific-Popup": 10139, "yudai/gotty": 10138, "jwilm/alacritty": 10137, "cjwirth/awesome-ios-ui": 10134, "davezuko/react-redux-starter-kit": 10132, "alcatraz/Alcatraz": 10130, "chriso/validator.js": 10129, "HubSpot/youmightnotneedjquery": 10125, "maxwellito/vivus": 10123, "parkjs814/AlgorithmVisualizer": 10120, "learn-anything/learn-anything": 10119, "dzenbot/DZNEmptyDataSet": 10115, "LightTable/LightTable": 10110, "jhipster/generator-jhipster": 10110, "graphql/graphql-js": 10101, "TeamStuQ/skill-map": 10099, "emilwallner/Screenshot-to-code-in-Keras": 10094, "norvig/pytudes": 10078, "php-fig/fig-standards": 10052, "octocat/Spoon-Knife": 10051, "tpope/vim-pathogen": 10049, "scala/scala": 10038, "bvaughn/react-virtualized": 10037, "go-martini/martini": 10033, "nikitavoloboev/my-mac-os": 10033, "PHPOffice/PHPExcel": 10032, "bmorelli25/Become-A-Full-Stack-Web-Developer": 10031, "labstack/echo": 10031, "jquery/jquery-mobile": 10023, "petehunt/webpack-howto": 10022, "winstonjs/winston": 10006, "veggiemonk/awesome-docker": 10006, "xdissent/ievms": 10006, "petehunt/react-howto": 10001, "keen/dashboards": 10001, "geeeeeeeeek/electronic-wechat": 9988, "littlecodersh/ItChat": 9975, "dawnlabs/carbon": 9958, "PowerShell/PowerShell": 9955, "jamiebuilds/the-super-tiny-compiler": 9942, "vurtun/nuklear": 9937, "acdlite/recompose": 9935, "robbiehanson/CocoaAsyncSocket": 9927, "sinatra/sinatra": 9924, "kangax/fabric.js": 9921, "you-dont-need/You-Dont-Need-JavaScript": 9918, "daimajia/AndroidSwipeLayout": 9889, "kataras/iris": 9880, "Automattic/wp-calypso": 9876, "allenwong/30DaysofSwift": 9876, "so-fancy/diff-so-fancy": 9852, "tmux/tmux": 9848, "jaredreich/pell": 9833, "vuetifyjs/vuetify": 9832, "SheetJS/js-xlsx": 9831, "fabric/fabric": 9828, "BrowserSync/browser-sync": 9826, "goldfire/howler.js": 9803, "greenrobot/greenDAO": 9775, "Seldaek/monolog": 9771, "flatpickr/flatpickr": 9769, "WickyNilliams/headroom.js": 9768, "date-fns/date-fns": 9736, "openfaas/faas": 9713, "revel/revel": 9708, "nicolargo/glances": 9701, "JakeWharton/ViewPagerIndicator": 9701, "google/ExoPlayer": 9689, "addyosmani/backbone-fundamentals": 9675, "handsontable/handsontable": 9672, "gdi2290/angular-starter": 9664, "go-kit/kit": 9651, "scrooloose/nerdtree": 9651, "cmusatyalab/openface": 9650, "CosmicMind/Material": 9648, "DmitryBaranovskiy/raphael": 9644, "tensorflow/magenta": 9635, "sebastianbergmann/phpunit": 9630, "jsdom/jsdom": 9629, "janpaepke/ScrollMagic": 9625, "facebook/yoga": 9620, "jessepollak/card": 9618, "karma-runner/karma": 9613, "OAI/OpenAPI-Specification": 9612, "zeit/pkg": 9598, "scottjehl/picturefill": 9590, "imathis/octopress": 9556, "dexteryy/spellbook-of-modern-webdev": 9552, "tj/co": 9539, "redox-os/redox": 9533, "salomonelli/best-resume-ever": 9531, "gaearon/redux-devtools": 9529, "gabrielecirulli/2048": 9525, "phacility/phabricator": 9517, "facebook/stetho": 9515, "shuzheng/zheng": 9510, "css-modules/css-modules": 9510, "fish-shell/fish-shell": 9510, "kesenhoo/android-training-course-in-chinese": 9501, "DrKLO/Telegram": 9494, "hdodenhof/CircleImageView": 9491, "vuejs/vue-router": 9474, "metabase/metabase": 9427, "Microsoft/dotnet": 9425, "realm/realm-java": 9419, "NodeRedis/node_redis": 9413, "erikras/redux-form": 9409, "svg/svgo": 9395, "ochococo/Design-Patterns-In-Swift": 9393, "eczarny/spectacle": 9371, "jobbole/awesome-python-cn": 9360, "hapijs/hapi": 9348, "tpope/vim-fugitive": 9343, "nodemailer/nodemailer": 9339, "jakevdp/PythonDataScienceHandbook": 9325, "odoo/odoo": 9315, "kailashahirwar/cheatsheets-ai": 9300, "peachananr/onepage-scroll": 9295, "gztchan/awesome-design": 9294, "tabler/tabler": 9292, "Konloch/bytecode-viewer": 9287, "joewalnes/websocketd": 9278, "google/flatbuffers": 9276, "daimajia/AndroidViewAnimations": 9262, "fivethirtyeight/data": 9259, "hehonghui/android-tech-frontier": 9259, "googlesamples/android-UniversalMusicPlayer": 9258, "signalapp/Signal-Android": 9255, "pockethub/PocketHub": 9238, "Tencent/wepy": 9237, "imakewebthings/waypoints": 9226, "roots/sage": 9225, "react-community/create-react-native-app": 9213, "dotnet/coreclr": 9205, "mxcl/PromiseKit": 9204, "nlohmann/json": 9203, "kevinzhow/PNChart": 9193, "elastic/kibana": 9173, "celery/celery": 9164, "aria2/aria2": 9160, "ethereum/wiki": 9159, "dotnet/roslyn": 9159, "gollum/gollum": 9141, "realm/SwiftLint": 9130, "spree/spree": 9129, "philipwalton/flexbugs": 9118, "paperjs/paper.js": 9113, "mikepenz/MaterialDrawer": 9107, "js-cookie/js-cookie": 9099, "luongvo209/Awesome-Linux-Software": 9095, "StevenBlack/hosts": 9094, "FelisCatus/SwitchyOmega": 9091, "obsproject/obs-studio": 9075, "nathanmarz/storm": 9075, "NetEase/pomelo": 9053, "winterbe/java8-tutorial": 9048, "matomo-org/matomo": 9041, "bitcoinbook/bitcoinbook": 9037, "matryer/bitbar": 9035, "Matt-Esch/virtual-dom": 9034, "stylus/stylus": 9031, "google/python-fire": 9024, "apenwarr/sshuttle": 9023, "channelcat/sanic": 9018, "vim-syntastic/syntastic": 9011, "Flipboard/FLEX": 9009, "spotify/luigi": 9006, "karpathy/convnetjs": 8990, "lerna/lerna": 8989, "fgnass/spin.js": 8986, "systemjs/systemjs": 8985, "phanan/koel": 8965, "kelseyhightower/kubernetes-the-hard-way": 8962, "palantir/blueprint": 8956, "ncw/rclone": 8956, "hashicorp/vault": 8951, "liaohuqiu/android-Ultra-Pull-To-Refresh": 8950, "Wox-launcher/Wox": 8948, "libuv/libuv": 8947, "explosion/spaCy": 8938, "vasanthk/react-bits": 8938, "tonybeltramelli/pix2code": 8937, "thoughtbot/paperclip": 8936, "tj/n": 8931, "binhnguyennus/awesome-scalability": 8922, "metafizzy/isotope": 8921, "dvajs/dva": 8919, "acgotaku/BaiduExporter": 8917, "yeoman/yeoman": 8915, "bbatsov/rubocop": 8903, "sdelements/lets-chat": 8896, "rwaldron/johnny-five": 8893, "javve/list.js": 8888, "apache/incubator-weex": 8884, "slimphp/Slim": 8869, "getredash/redash": 8869, "aFarkas/html5shiv": 8867, "uNetworking/uWebSockets": 8866, "orhanobut/logger": 8864, "google/grumpy": 8860, "philc/vimium": 8843, "phalcon/cphalcon": 8841, "knockout/knockout": 8837, "elastic/logstash": 8836, "getgrav/grav": 8834, "feathersjs/feathers": 8832, "powerline/powerline": 8825, "sorin-ionescu/prezto": 8825, "daylerees/colour-schemes": 8815, "Ramotion/animated-tab-bar": 8790, "audreyr/favicon-cheat-sheet": 8781, "forkingdog/UITableView-FDTemplateLayoutCell": 8775, "francistao/LearningNotes": 8771, "google/WebFundamentals": 8766, "fogleman/primitive": 8765, "EnterpriseQualityCoding/FizzBuzzEnterpriseEdition": 8762, "dcloudio/mui": 8762, "v8/v8": 8760, "saltstack/salt": 8756, "ant-design/ant-design-pro": 8743, "briannesbitt/Carbon": 8741, "Rochester-NRT/RocAlphaGo": 8731, "datasciencemasters/go": 8731, "buger/goreplay": 8713, "ccampbell/mousetrap": 8710, "gaearon/react-hot-loader": 8702, "deeplearning4j/deeplearning4j": 8701, "bazelbuild/bazel": 8699, "WebAssembly/design": 8694, "ksoichiro/Android-ObservableScrollView": 8689, "Netflix/falcor": 8688, "akveo/blur-admin": 8682, "byoungd/English-level-up-tips-for-Chinese": 8681, "zyedidia/micro": 8674, "recharts/recharts": 8673, "navasmdc/MaterialDesignLibrary": 8668, "docker/kitematic": 8663, "jquery-validation/jquery-validation": 8650, "chalk/chalk": 8645, "fullstackio/FlappySwift": 8637, "airbnb/lottie-react-native": 8634, "chrisbanes/Android-PullToRefresh": 8633, "Microsoft/monaco-editor": 8628, "ApolloAuto/apollo": 8624, "OpenEmu/OpenEmu": 8623, "basecamp/trix": 8619, "markets/awesome-ruby": 8605, "Hacker0x01/hacker101": 8597, "NodeBB/NodeBB": 8596, "zenorocha/alfred-workflows": 8590, "nfarina/homebridge": 8583, "nodejitsu/node-http-proxy": 8581, "thoughtbot/bourbon": 8577, "jinzhu/gorm": 8573, "golang/dep": 8567, "wulkano/kap": 8560, "BurntSushi/ripgrep": 8558, "apple/swift-evolution": 8558, "dropbox/zxcvbn": 8556, "Moya/Moya": 8546, "ConnorAtherton/loaders.css": 8531, "mperham/sidekiq": 8528, "MithrilJS/mithril.js": 8523, "raywenderlich/swift-style-guide": 8522, "NUKnightLab/TimelineJS": 8517, "necolas/react-native-web": 8516, "gfwlist/gfwlist": 8514, "mxgmn/WaveFunctionCollapse": 8499, "ChristosChristofidis/awesome-deep-learning": 8490, "chentsulin/electron-react-boilerplate": 8488, "gaearon/redux-thunk": 8485, "mishoo/UglifyJS2": 8481, "shichuan/javascript-patterns": 8477, "carhartl/jquery-cookie": 8474, "onevcat/VVDocumenter-Xcode": 8468, "alibaba/p3c": 8458, "facebook/Shimmer": 8456, "swoole/swoole-src": 8452, "alexjc/neural-doodle": 8442, "FezVrasta/popper.js": 8438, "sqlitebrowser/sqlitebrowser": 8426, "chinese-poetry/chinese-poetry": 8425, "guillaumepotier/Parsley.js": 8424, "ruby-grape/grape": 8423, "jordansissel/fpm": 8422, "kubernetes/minikube": 8421, "akka/akka": 8420, "GeekyAnts/NativeBase": 8416, "StackExchange/Dapper": 8389, "purifycss/purifycss": 8386, "coryhouse/react-slingshot": 8381, "slackhq/SlackTextViewController": 8372, "irungentoo/toxcore": 8366, "boltdb/bolt": 8357, "Idnan/bash-guide": 8345, "Automattic/_s": 8313, "mhinz/vim-galore": 8310, "teamcapybara/capybara": 8310, "fatih/vim-go": 8306, "rasbt/python-machine-learning-book": 8304, "facebook/infer": 8298, "sindresorhus/pageres": 8296, "cyclejs/cyclejs": 8278, "mbadolato/iTerm2-Color-Schemes": 8274, "qrohlf/trianglify": 8257, "connors/photon": 8255, "jikexueyuanwiki/tensorflow-zh": 8251, "afaqurk/linux-dash": 8250, "websockets/ws": 8249, "nosir/cleave.js": 8238, "arasatasaygin/is.js": 8236, "aFarkas/lazysizes": 8228, "1c7/chinese-independent-developer": 8199 } ================================================ FILE: index.js ================================================ require('dotenv').config() const koa = require('koa') const route = require('koa-route') const serve = require('koa-static') const fs = require('mz/fs') const {createTextSvg} = require('./svg') const {total, rateLimit} = require('./render') const queue = require('./queue') const page = require('./page') const ttl = require('./ttl') const app = new koa() queue.worker() app.use(serve('public')); app.use(route.get('/:owner/:name.svg', async (ctx, owner, name) => { ctx.type = 'image/svg+xml; charset=utf-8' const repo = `${owner}/${name}` const path = `svg/${owner}/${name}.svg`.toLowerCase() let stats try { const maxAge = ttl(repo) stats = await fs.stat(path) ctx.set('Last-Modified', stats.mtime.toUTCString()) ctx.set('Cache-Control', 'max-age=' + maxAge) ctx.body = fs.createReadStream(path) const now = new Date().getTime() const mtime = stats.mtime.getTime() if (now - mtime > maxAge * 1000) { queue.push(path, owner, name) } } catch (err) { if (err.code === 'ENOENT') { queue.push(path, owner, name) const p = total.get(path) if (p) { ctx.body = createTextSvg(`⚡️ loading stars ${p}%`) } else { ctx.body = createTextSvg(`👋️ waiting in queue ${queue.indexOf(path) + 1}`) } } else { ctx.body = createTextSvg(`⚠️ error`) } } })) app.use(route.get('/', async (ctx) => { if (ctx.query.repo) { ctx.redirect('/' + ctx.query.repo) } else { ctx.type = 'text/html' ctx.body = page.index() } })) app.use(route.get('/:owner/:name', async (ctx, owner, name) => { ctx.type = 'text/html' ctx.body = page.repo({owner, name}) })) app.use(route.get('/status', async (ctx) => { ctx.type = 'application/json' ctx.body = { queueSize: queue.size(), processing: [...total.keys()], rateLimit: rateLimit.remaining } })) const port = process.env.PORT || 3000 app.listen(port) console.log('App started on port ' + port) ================================================ FILE: package.json ================================================ { "name": "@medv/spark", "version": "1.0.0", "description": "GitHub Stars Sparklines", "main": "index.js", "author": "Anton Medvedev ", "repository": "antonmedv/spark", "license": "MIT", "dependencies": { "delay": "^2.0.0", "dotenv": "^5.0.1", "koa": "^2.5.0", "koa-route": "^3.2.0", "koa-static": "^4.0.2", "mz": "^2.7.0", "r2": "^2.0.1" } } ================================================ FILE: page.js ================================================ const style = require('fs').readFileSync('style.css') const rand = () => Math.round(100 * Math.random()) const layout = (content) => ` ⚡️ GitHub Stars Sparklines
${content}
` const box = (owner, name) => { let title = owner + '/' + name if (title.length > 20) { title = name } return `
${title}
` } const a = (path) => { const [owner, name] = path.split('/') return ` ${box(owner, name)} ` } const h1 = () => `

⚡️ Spark GitHub Stars Sparklines

` exports.index = () => layout(` ${h1()}
${a('facebook/react')} ${a('angular/angular')} ${a('vuejs/vue')} ${a('freeCodeCamp/freeCodeCamp')} ${a('jquery/jquery')} ${a('twbs/bootstrap')} ${a('rails/rails')} ${a('FortAwesome/Font-Awesome')} ${a('jashkenas/backbone')} ${a('php/php-src')} ${a('nodejs/node')} ${a('torvalds/linux')} ${a('moby/moby')} ${a('laravel/laravel')} ${a('reactjs/redux')} ${a('d3/d3')} ${a('axios/axios')} ${a('robbyrussell/oh-my-zsh')} ${a('facebook/react-native')} ${a('meteor/meteor')}
`) exports.repo = ({owner, name}) => layout(` ${h1()}
${box(owner, name)}

The Sparkline shows GitHub stars velocity of ${owner + '/' + name} repo for the entire lifetime of the repository.

Add the Sparkline to repo's readme, copy markdown code below.

[![Sparkline](https://stars.medv.io/${owner + '/' + name}.svg)](https://stars.medv.io/${owner + '/' + name})
`) ================================================ FILE: queue.js ================================================ const {render} = require('./render') const delay = require('delay') const queue = [] const set = new Set() function size() { return queue.length } async function worker() { do { try { const work = queue.shift() if (work) { const [path, owner, name] = work await render(path, owner, name) set.delete(path) } else { await delay(100) } } catch (err) { console.error(err) } } while (true) } function push(path, owner, name) { if (!set.has(path)) { queue.push([path, owner, name]) set.add(path) } } function indexOf(path) { return queue.findIndex(([p]) => path === p) } module.exports = {worker, push, size, indexOf} ================================================ FILE: render.js ================================================ const {dirname} = require('path') const fs = require('mz/fs') const {fetch} = require('./api') const {createSvg} = require('./svg') const total = new Map() const query = ` query($owner: String!, $name: String!, $endCursor: String) { repository(owner: $owner, name: $name) { stargazers(first: 100, after: $endCursor) { totalCount edges { starredAt } pageInfo { hasNextPage endCursor } } } rateLimit { remaining } } ` let rateLimit = { remaining: 5000 } async function render(path, owner, name) { total.set(path, 0) const data = await fetch(query, {owner, name}) if (data.repository) { let { stargazers: { totalCount, edges: dates, pageInfo: {hasNextPage, endCursor} } } = data.repository while (hasNextPage) { total.set(path, Math.round(100 * dates.length / totalCount)) console.log(`${owner}/${name}: ${total.get(path)}%`) const data = await fetch(query, {owner, name, endCursor}) hasNextPage = data.repository.stargazers.pageInfo.hasNextPage endCursor = data.repository.stargazers.pageInfo.endCursor rateLimit.remaining = data.rateLimit.remaining dates = dates.concat(data.repository.stargazers.edges) } dates = dates.map(({starredAt}) => +(new Date(starredAt))) const svg = createSvg(dates) const dir = dirname(path) await fs.exists(dir) || await fs.mkdir(dir) fs.writeFile(path, svg) } total.delete(path) } module.exports = {render, total, rateLimit} ================================================ FILE: style.css ================================================ html { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } *, *:before, *:after { -webkit-box-sizing: inherit; -moz-box-sizing: inherit; box-sizing: inherit; padding: 0; margin: 0; } body { font-family: Helvetica, serif; font-size: 14px; line-height: 1.5; color: rgba(0, 0, 0, .65); background-color: #f9fbff; } .content { display: flex; flex-direction: column; margin: 40px auto; padding-left: 20px; padding-right: 20px; max-width: 1400px; } h1 { margin-bottom: 50px; font-size: 32px; } h1 > span { font-size: 16px; font-weight: normal; } @media only screen and (max-device-width: 350px) { h1 > span { display: block; } } .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); grid-gap: 15px 15px; } a { color: inherit; text-decoration: none; } .box { display: flex; flex-direction: column; padding: 10px 15px; white-space: nowrap; font-size: 16px; font-weight: 500; color: inherit; background: #fff; border-radius: 3px; -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24); box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24); -webkit-transition: all .3s cubic-bezier(.25, .8, .25, 1); -o-transition: all .3s cubic-bezier(.25, .8, .25, 1); transition: all .3s cubic-bezier(.25, .8, .25, 1); } .box:hover { text-decoration: none; color: inherit; -webkit-transform: scale(1.01); -ms-transform: scale(1.01); transform: scale(1.01); -webkit-box-shadow: 0 10px 20px rgba(0, 0, 0, .19), 0 6px 6px rgba(0, 0, 0, .23); box-shadow: 0 10px 20px rgba(0, 0, 0, .19), 0 6px 6px rgba(0, 0, 0, .23); } .name { font-weight: 500; font-size: 18px; margin-bottom: 5px; padding-left: 20px; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='currentColor' preserveAspectRatio='xMidYMid meet' height='1em' width='1em' viewBox='0 0 40 40' style='vertical-align: middle;'%3E%3Cg%3E%3Cpath d='m17.5 10h-2.5v2.5h2.5v-2.5z m0-5h-2.5v2.5h2.5v-2.5z m15-5h-25s-2.5 1.3-2.5 2.5v30s1.3 2.5 2.5 2.5h5v5l3.8-3.7 3.7 3.7v-5h12.5s2.5-1.2 2.5-2.5v-30s-1.2-2.5-2.5-2.5z m0 31.3c0 0.6-0.6 1.2-1.2 1.2h-11.3v-2.5h-7.5v2.5h-3.7s-1.3-0.7-1.3-1.2v-3.8h25v3.8z m0-6.3h-20v-22.5h20l0 22.5z m-15-5h-2.5v2.5h2.5v-2.5z m0-5h-2.5v2.5h2.5v-2.5z'%3E%3C/path%3E%3C/g%3E%3C/svg%3E"); background-position: 0 8px; background-repeat: no-repeat; overflow-x: hidden; -o-text-overflow: ellipsis; text-overflow: ellipsis; } .spark { width: 200px; align-self: center; } .one { display: flex; flex-direction: column; } .one-link { display: inline-block; align-self: center; margin-top: 30px; margin-bottom: 60px; transform: scale(1.5); } .one > p { display: block; font-size: 16px; margin-top: 10px; max-width: 600px; align-self: center; } code { margin-top: 40px; padding: 10px 15px; max-width: 600px; overflow-x: scroll; align-self: center; white-space: nowrap; font-family: monospace; background-color: #eeeeee; } @media only screen and (max-device-width: 812px) { code { width: 100%; max-width: none; } } .add-repo { margin-top: 90px; align-self: center; } .add-repo label { display: block; padding: 5px; font-size: 16px; text-align: center; } .add-repo input[type="text"] { border: none; border-radius: 4px; outline: none; padding: 10px 17px; font-family: monospace; font-size: 16px; width: 200px; background-color: #fff; box-shadow: 2px 5px 10px rgb(228, 228, 228); } .add-repo button { margin: 10px; padding: 12px 12px; cursor: pointer; user-select: none; text-align: center; white-space: nowrap; border: 0 none; border-radius: 4px; font-size: 13px; font-weight: 500; line-height: 1.3; -webkit-appearance: none; -moz-appearance: none; box-shadow: 2px 5px 10px rgb(228, 228, 228); color: #7e8091; background-color: #fff; transition: background-color 150ms linear; } .add-repo button:hover { background-color: #f2f2f2; } .r { top: 0; left: 0; right: 0; bottom: 0; position: absolute; z-index: -1; overflow: hidden; } .r1, .r2, .r3, .r4{ position: relative; width: 50px; height: 50px; border-radius: 100%; border: solid 12px #fc607f; } .r2 { border-color: #4580fe; } .r3 { border-color: #fcb84d; } .r4 { border-color: #78faca; border-radius: 0; } ================================================ FILE: svg.js ================================================ const height = 30 const x0 = 5 const y0 = 40 const steps = 40 const dx = 5 const basis = 1.5 const gradient = ['#3023AE', '#C86DD7'] const round = num => Math.round(num * 100) / 100 const vec = (x, y) => ({ x, y, toString() { return `${round(this.x)} ${round(this.y)}` } }) const norm = v => Math.sqrt(v.x * v.x + v.y * v.y) const unit = v => { const n = norm(v) return vec(basis * v.x / n, basis * v.y / n) } const add = (a, b) => vec(a.x + b.x, a.y + b.y) const sub = (a, b) => vec(a.x - b.x, a.y - b.y) const end = px => px[px.length - 1] function createSvg(data) { if (data.length <= steps) { return createTextSvg('⭐️ not enough stars') } const min = data[0] const max = end(data) const step = (max - min) / steps const yx = [] { let i = min let count = 0 for (let d of data) { if (d < i + step) { count++ } else { yx.push(count) count = 1 i += step } } } const scale = Math.max(...yx) / height { for (let i = 0; i < yx.length; i++) { yx[i] = round(yx[i] / scale) } } const points = [] { let x = x0 for (let y of yx) { x += dx points.push(vec(x, y0 - y)) } } const p0 = vec(x0, y0) const p1 = points[0] const p2 = points[1] const c1 = add(p0, unit(sub(p1, p0))) const c2 = add(p1, unit(sub(p0, p2))) let path = `M${p0} C ${c1}, ${c2}, ${p1}` for (let i = 1; i < points.length; i++) { const p0 = points[i - 1] const p1 = points[i] const p2 = points[i + 1] || p1 const c = add(p1, unit(sub(p0, p2))) path += ` S ${c}, ${p1}` } const pN = end(points) return ` ` } function createTextSvg(text) { return ` ${text} ` } module.exports = {createSvg, createTextSvg} ================================================ FILE: sync.js ================================================ require('dotenv').config() const fs = require('mz/fs') const {fetch} = require('./api') const query = ` query ($endCursor: String) { search(type: REPOSITORY, query: "stars:>8000", first: 100, after: $endCursor) { repositoryCount nodes { ... on Repository { owner { login } name stargazers { totalCount } } } pageInfo { hasNextPage endCursor } } } ` async function main() { let { search: { repositoryCount, nodes, pageInfo: {hasNextPage, endCursor} } } = await fetch(query) while (hasNextPage) { const pt = Math.round(100 * nodes.length / repositoryCount) console.log(`loading ${pt}% ${nodes.length}/${repositoryCount}`) const data = await fetch(query, {endCursor}) endCursor = data.search.pageInfo.endCursor hasNextPage = data.search.pageInfo.hasNextPage nodes = nodes.concat(data.search.nodes) } console.log('Total repos: ' + nodes.length) const db = {} for (let node of nodes) { const { owner: {login}, name, stargazers: {totalCount} } = node db[login + '/' + name] = totalCount } fs.writeFile('db.js', `module.exports = ${JSON.stringify(db, null, 2)}\n`) } main().catch(console.log) ================================================ FILE: ttl.js ================================================ const db = require('./db') function ttl(repo) { const stars = db[repo] || 0 if (stars > 10000) { return 604800 // one week } else if (stars > 8000) { return 3 * 86400 // three days } else { return 86400 // one day } } module.exports = ttl