{"id":20182115,"url":"https://github.com/nichoth/node-style-adventure","last_synced_at":"2026-07-14T02:32:19.615Z","repository":{"id":77400114,"uuid":"115819548","full_name":"nichoth/node-style-adventure","owner":"nichoth","description":"Demo of functional and object oriented patterns","archived":false,"fork":false,"pushed_at":"2018-02-10T20:16:45.000Z","size":15,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-10T06:37:25.613Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nichoth.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-12-30T19:49:44.000Z","updated_at":"2018-07-19T17:39:14.000Z","dependencies_parsed_at":null,"dependency_job_id":"16bffe66-2195-429e-bfe4-abc1b2f706e0","html_url":"https://github.com/nichoth/node-style-adventure","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/nichoth/node-style-adventure","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nichoth%2Fnode-style-adventure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nichoth%2Fnode-style-adventure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nichoth%2Fnode-style-adventure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nichoth%2Fnode-style-adventure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nichoth","download_url":"https://codeload.github.com/nichoth/node-style-adventure/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nichoth%2Fnode-style-adventure/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35443979,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-14T02:00:06.603Z","response_time":114,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-14T02:37:42.653Z","updated_at":"2026-07-14T02:32:19.596Z","avatar_url":"https://github.com/nichoth.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"Take multiple s3 uploads, and return progress events for the total progress for all the uploads combined. Each s3 upload object emits events like `{ loaded: 1, total 10 }`, but `total` may be undefined because it has not yet been calculated.\n\n## Event emitter\n\n\u003cdetails\u003e\n\n```js\nvar inherits = require('util').inherits\nvar EE = require('events').EventEmitter\n\n// take multiple s3 uploads and return a single event emitter with\n// total progress\nfunction UploadProgress (uploads) {\n    if (!(this instanceof UploadProgress)) {\n        return new UploadProgress(uploads);\n    }\n    this.uploads = uploads;\n    var bus = this;\n    var totals = uploads.map(() =\u003e null);\n    var sum;\n    var progresses = uploads.map(() =\u003e 0);\n    var isReady = false;\n\n    this._stateListeners = [];\n    this._progressListener = emitProgress;\n    var self = this;\n\n    uploads.forEach(function (upload, i) {\n        self._stateListeners.push(getState);\n        upload.on('httpUploadProgress', getState);\n\n        function getState (data) {\n            progresses[i] = data.loaded;\n            if (isReady) return;  // we have all the totals already\n            if (data.total) totals[i] = data.total;\n            if (totals.every(Boolean)) {\n                isReady = true;\n                sum = totals.reduce((_sum, n) =\u003e _sum + n);\n            }\n        }\n    });\n\n    // emit the overall progress\n    var prev;\n    uploads.forEach(function (upload, i) {\n        upload.on('httpUploadProgress', emitProgress);\n    });\n\n    function emitProgress (data) {\n        if (!isReady) return;\n        var prog = progresses.reduce((acc, n) =\u003e acc + n);\n        var percent = Math.floor(prog / sum * 100);\n        if (prev === percent) return;  // filter duplicate values\n        prev = percent;\n        bus.emit('progress', percent);\n    }\n\n    EE.call(this);\n}\ninherits(UploadProgress, EE);\n\nUploadProgress.prototype.close = function () {\n    var self = this;\n    this.uploads.forEach(function (upload, i) {\n        upload.removeListener('httpUploadProgress', self._stateListeners[i]);\n        upload.removeListener('httpUploadProgress', self._progressListener);\n    });\n    this.removeAllListeners();\n};\n\nmodule.exports = UploadProgress;\n```\n\n\u003c/details\u003e\n\n\n\n## pull stream\n\nWhy is this version smaller than the OO example? Where is the the equivalent state in here?\n\n\u003cdetails\u003e\n\n```js\nvar S = require('pull-stream')\nvar _ = require('pull-stream-util')\n\nfunction Progress (uploads) {\n    var uploadStreams = uploads.map(_.fromEvent('httpUploadProgress'))\n\n    var progress$ = S(\n        _.combineLatest(uploadStreams),\n\n        _.scan(function (state, evs) {\n            var sum = state.sum || (evs.every(ev =\u003e ev.total) ?\n                evs.reduce((_sum, ev) =\u003e _sum + ev.total, 0) :\n                null)\n\n            if (!sum) return state\n\n            var prog = evs.reduce((sum, ev) =\u003e sum + ev.loaded, 0)\n            return { sum, percent: Math.floor(prog / sum * 100) }\n        }, { sum: null, percent: null }),\n\n        S.filter(state =\u003e state.sum),\n        S.map(state =\u003e state.percent)\n    )\n\n    progress$.close = function () {\n        uploadStreams.forEach(up =\u003e up.end())\n    }\n\n    return progress$\n}\n\nmodule.exports = Progress\n```\n\n\u003c/details\u003e\n\n## benchmark\n\nWhich will be faster? What is a good way to test this? [/test/bench/index.js](/test/bench/index.js)\n\n\u003cdetails\u003e\n\n```\nbenchStream*10000: 463.748ms\nbenchEmitter*10000: 255.184ms\nbenchStream*10000: 499.722ms\nbenchEmitter*10000: 276.881ms\n```\n\n\u003c/details\u003e\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnichoth%2Fnode-style-adventure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnichoth%2Fnode-style-adventure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnichoth%2Fnode-style-adventure/lists"}