{"id":43536446,"url":"https://github.com/behnamazimi/js-service-wrapper","last_synced_at":"2026-02-03T16:51:55.043Z","repository":{"id":57283605,"uuid":"268801202","full_name":"behnamazimi/js-service-wrapper","owner":"behnamazimi","description":"Promise based collective service wrapper with queue support for browsers and NodeJS","archived":false,"fork":false,"pushed_at":"2021-08-13T05:46:24.000Z","size":117,"stargazers_count":9,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-22T04:43:59.742Z","etag":null,"topics":["request-queuing","service-queue","service-wrapper","task-queue"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/behnamazimi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-06-02T12:57:06.000Z","updated_at":"2023-07-13T16:17:39.000Z","dependencies_parsed_at":"2022-09-04T19:20:43.210Z","dependency_job_id":null,"html_url":"https://github.com/behnamazimi/js-service-wrapper","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/behnamazimi/js-service-wrapper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/behnamazimi%2Fjs-service-wrapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/behnamazimi%2Fjs-service-wrapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/behnamazimi%2Fjs-service-wrapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/behnamazimi%2Fjs-service-wrapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/behnamazimi","download_url":"https://codeload.github.com/behnamazimi/js-service-wrapper/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/behnamazimi%2Fjs-service-wrapper/sbom","scorecard":{"id":230541,"data":{"date":"2025-08-11","repo":{"name":"github.com/behnamazimi/js-service-wrapper","commit":"1fcab9fd7a789aecd8b9ead82134b1238d17cc04"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-17T04:45:52.660Z","repository_id":57283605,"created_at":"2025-08-17T04:45:52.660Z","updated_at":"2025-08-17T04:45:52.660Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29049970,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-03T15:43:47.601Z","status":"ssl_error","status_checked_at":"2026-02-03T15:43:46.709Z","response_time":96,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["request-queuing","service-queue","service-wrapper","task-queue"],"created_at":"2026-02-03T16:51:54.424Z","updated_at":"2026-02-03T16:51:55.031Z","avatar_url":"https://github.com/behnamazimi.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JS Service Wrapper\nA promise based collective service wrapper with queue support which totally works in browser and/or Node.js environment\n\nSometimes you need to pass your service functions from a shared pipe and call some actions on all of them.\n Or maybe you want to add all of your services to a queue that supports parallel and pending tasks.\n\nNeeds that I mentioned above are common especially when you are using an http-request client like \n[**axios**](https://github.com/axios/axios), [**fetch**](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), \nor [**superagent**](https://github.com/visionmedia/superagent). Lots of the fetching could fire in the same time and \nif you want to check a common situation for all of them it could be very frustrating. In these conditions,\n you can use **JS Service Wrapper**.\n\n## Getting Started\n\n### Installation\n\nUsing npm:\n```\nnpm install js-service-wrapper\n```\n\nUsing yarn:\n```\nyarn add js-service-wrapper\n```\n\nUsing unpkg CDN\n```\n\u003cscript src=\"http://unpkg.com/js-service-wrapper/lib/js-service-wrapper.min.js\"\u003e\u003c/script\u003e\n```****\n\n### Usage\n\nFirst of all you need to import `js-service-wrapper` in your project.\n```javascript\nconst {ServiceWrapper, ClientHandler, HOOKS} = require(\"js-service-wrapper\");\n\n// ES Module\nimport {ServiceWrapper, ClientHandler, HOOKS} from \"js-service-wrapper\";\n```\n\n`ServiceWrapper` is the main object of our util. You need to initialize it, and it's enough to init it once in your project.\n```javascript\nServiceWrapper\n    .init({\n        // `client` is the function that call inside `ClientHandler`\n        // default value is null\n        client: axios, \n        \n        // `queue` is for determining that your service wrapper should active queue or not \n        // the queue is disabled by default\n        queue: true,\n    \n        // if this will be true, queue will log details in different stages \n        // default value is false\n        queueLogs: true,\n        \n        // set the default parallel value of fireOptions \n        // default value is true\n        defaultParallelStatus: true,\n    })\n    // a function that execute to validate the result of main promise\n    // if the result of this function be false, the promise will reject\n    // default is a function that always returns true\n    .setResolveValidation(res =\u003e res.status === \"ok\");\n```\n\nThe client that you set on the `ServiceWrapper` is the most important part of the initialization. It will be call \ninside the `ClientHandler` and will return a promise. Also, you can do a validation on the promise result by define \na promise response validation method, `setResolveValidation`. It will execute after promise done and check if the \nresult is exactly what you want or not. If the resolve validation return false, so the service will reject. \n\nThe default value of `resolveValidation` is a function that returns `true`.\n\nHere is an example of wrapping that calls `axios` as client because we set it as the `client` on `init`.\n```javascript\nnew ClientHandler({url: \"https://reqres.in/api/users\"})\n    .fire({parallel: false})\n    .then(res =\u003e {\n        // handle result\n        console.log(\"users fetched\");\n    })\n    .catch(err =\u003e {\n        /// handle error\n        console.log(err);\n    })\n```\n\nYou can send an object as fire-options to the `fire` method. the most important property on it is the `parallel`. \nIf you not send the parallel status to the `fire`, handler will take it from `defaultParallelStatus` which you send on the `init`.\n\n### Hooks\n\nWe have the chance to intervene in the above trend at different stages by **hooks**. \nHooks are some functions that execute in places we defined, you can think of hooks as events triggered at \nspecial points. Each hook has a name (event type) and a function that executes (listener). \n\nLet's set some hooks to the service wrapper.\n\n```javascript\nServiceWrapper\n         // get result and return the data property\n        .setHook(HOOKS.BEFORE_RESOLVE, res =\u003e res.data)\n        // this method will call on all successes.\n        .setHook(HOOKS.AFTER_SUCCESS, res =\u003e {\n            console.log(res);\n        });\n```\n\nThe hooks that you set on the `ServiceWrapper` will execute on all services. In the above example, the first one will get \nthe result and return the `data` property of it, and the second one just receives the result after client execution succeed and log it.\n\nThere are six pre-defined hooks that you can set them to the `ServiceWrapper` or on each `ClientHandler`s to affect the services.\n 1. `HOOKS.BEFORE_FIRE` calls before the client service calling. This hook is not async, and the fire will not wait for this. \n 2. `HOOKS.BEFORE_RESOLVE` calls when the service client promise is resolving. The value that it returns will send as the resolve parameter.\n 3. `HOOKS.BEFORE_REJECT` calls when the service client promise is rejecting. The value that it returns will send as the reject parameter.\n 4. `HOOKS.AFTER_SUCCESS` calls exactly before the resolve and this is not async to.  \n 5. `HOOKS.AFTER_FAIL` calls exactly before the reject and this is not async to.  \n 6. `HOOKS.UPDATE_SERVICE_CONFIG` with this hook you can update the service config before fire.\n \n Each `ClientHandler` could have its special hook set, and its hooks will override the global hooks that have set on the `ServiceWrapper`.\n Also, you can set the special `client` for each `ClientHandler`. Here is an example.\n ```javascript\nnew ClientHandler(clientConfig)\n    .setClient(manualClient) \n    .setHook(HOOKS.BEFORE_RESOLVE, res =\u003e res.body) // get result and return the data property\n    .fire({parallel: true})\n    .then(res=\u003e {\n        //...\n    })\n``` \n \n### Queue\n \n If you active the **queue** on initialization, so you can specify the behavior of each service in the queue, and determine \n that your service should be parallel beside other services or pending. To do that you should pass options to  \n the `fire` method and set the value of the `parallel` property as `true` or `false`. The parallel service will fire \n immediately after adding to the queue, but the pending service waits for its queue. Here is an example of a parallel \n and a pending service definition.\n \n ```javascript\n// this will fire immediately after adding to the queue\nnew ClientHandler({url: \"https://reqres.in/api/users\"})\n    .fire({parallel: true})\n    .then(res=\u003e {\n        //...\n    })\n\n\n// this will wait for its turn on queue\nnew ClientHandler({url: \"https://reqres.in/api/users\"})\n    .fire({parallel: false})\n    .then(res=\u003e {\n        //...\n    })\n```\n\n#### Cancel Fired Service\nYou can cancel the **pending** services of the queue. To do this, you need to put your wrapper instance in a variable before `fire`, \nand call the `cancel` method of it when you want, like this.\n\n```javascript\nconst cancelableClient = new ClientHandler()\n    .setClient(fakeClient)\n    .setHook(HOOKS.BEFORE_RESOLVE, res =\u003e res)\n\ncancelableClient\n    .fire({parallel: false, id: \"CANCELABLE_USER_FETCH\"})\n    .then((res) =\u003e {\n        console.log(\"CANCELABLE_USER_FETCH done\");\n    })\n    .catch(err =\u003e {\n        console.log(err);\n    })\n\ncancelableClient.cancel()\n``` \n\nAlso, you can ask the `ServiceWrapper` to cancel your service and remove it from the queue by its `cancelService` method. \n\n```javascript\nServiceWrapper.cancelService(serviceID);\n```\n\n## Full Example\n```javascript\nconst {ServiceWrapper, ClientHandler, HOOKS} = require(\"js-service-wrapper\");\n\nfunction fakeClient() {\n    return new Promise(resolve =\u003e {\n        setTimeout(() =\u003e {\n            resolve({data: {a: 1, b: 2}, status: 200});\n        }, 1000)\n    })\n}\n\nServiceWrapper\n    .init({\n        client: axios,\n        queue: true,\n        queueLogs: true,\n    })\n    .setHook(HOOKS.BEFORE_RESOLVE, res =\u003e res.data)\n    .setHook(HOOKS.AFTER_SUCCESS, (res, fireOptions) =\u003e {\n        // update auth token\n        console.log(`\\n=== after success ===\u003e\u003e ${fireOptions.id}`)\n    })\n    .setHook(HOOKS.AFTER_FAIL, err =\u003e {\n        // handle status\n        if (err.response.status === 401) {\n            console.log('redirect to /401')\n\n        } else if (err.response.status === 404) {\n            console.log('redirect to /404')\n\n        } else if (err.response.status === 500) {\n            console.log('redirect to /500')\n        }\n    })\n\n\nnew ClientHandler(\"https://reqres.in/api/users\")\n    .fire({parallel: false, id: \"ALL_USERS_FETCH\"})\n    .then((res) =\u003e {\n        console.log(\"ALL_USERS_FETCH done\");\n    })\n    .catch(err =\u003e {\n        console.log(err);\n    })\n\n\nnew ClientHandler(\"https://reqres.in/api/users/2\")\n    .fire({parallel: false, id: \"USER_2_FETCH\"})\n    .then(res =\u003e {\n        console.log(\"USER_2_FETCH done\");\n    })\n    .catch(err =\u003e {\n        console.log(err);\n    })\n\n\nconst cancelableService = new ClientHandler()\n    .setClient(fakeClient)\n    .setHook(HOOKS.BEFORE_RESOLVE, res =\u003e res)\n\ncancelableService\n    .fire({parallel: false, id: \"CANCELABLE_USER_FETCH\"})\n    .then((res) =\u003e {\n        console.log(\"CANCELABLE_USER_FETCH done\");\n    })\n    .catch(err =\u003e {\n        console.log(err);\n    })\n\n\nnew ClientHandler(\"https://reqres.in/api/users/3\")\n    .setClient(superagent)\n    .setHook(HOOKS.BEFORE_RESOLVE, res =\u003e res.body)\n    .fire({parallel: false, id: \"USER_3_FETCH\"})\n    .then((res) =\u003e {\n        console.log(\"USER_3_FETCH done\");\n    })\n    .catch(err =\u003e {\n        console.log(err);\n    })\n\nnew ClientHandler(\"https://reqres.in/api/users/4\")\n    .setClient(superagent)\n    .setHook(HOOKS.BEFORE_RESOLVE, res =\u003e res.body)\n    .fire({parallel: false, id: \"USER_4_FETCH\"})\n    .then((res) =\u003e {\n        console.log(\"USER_4_FETCH done\");\n    })\n    .catch(err =\u003e {\n        console.log(err);\n    })\n\n\n// try to cancel client\nif (cancelableService.cancel()) {\n    console.log(`*** Service ${cancelableService.id} canceled before fire ***`)\n}\n\nif (ServiceWrapper.cancelService(\"USER_3_FETCH\")) {\n    console.log(`*** Service USER_3_FETCH canceled before fire ***`)\n}\n```\n\nThis is the console result of the above example:\n```bash\n+ ADDED: ALL_USERS_FETCH\n* FIRED: ALL_USERS_FETCH [type: pending]\n+ ADDED: USER_2_FETCH\n+ ADDED: CANCELABLE_USER_FETCH\n+ ADDED: USER_3_FETCH\n+ ADDED: USER_4_FETCH\n- REMOVED: CANCELABLE_USER_FETCH\n*** Service CANCELABLE_USER_FETCH canceled before fire ***\n*** Service USER_3_FETCH canceled before fire ***\n\n=== after success ===\u003e\u003e ALL_USERS_FETCH\n- REMOVED: ALL_USERS_FETCH\n* FIRED: USER_2_FETCH [type: pending]\nALL_USERS_FETCH done\n\n=== after success ===\u003e\u003e USER_2_FETCH\n- REMOVED: USER_2_FETCH\n* FIRED: USER_4_FETCH [type: pending]\nUSER_2_FETCH done\n\n=== after success ===\u003e\u003e USER_4_FETCH\n- REMOVED: USER_4_FETCH\nUSER_4_FETCH done\n```\n\n### Tips\n * To use the `fetch` as your client function, you need to send the bound version of it as like `fetch.bind(window)`.\n * The `fireOptions` that you pass to the `fire` method is accessible in the hook methods as the second parameter.\n * The wrapper itself is promise base but the `client` function doesn't need to be a promise.\n\n### Contributing\nI would be grateful to those who helped me make the project truly perfect. So, feel free to contribute to the project.\n\n### License\n\n[MIT](https://github.com/behnamazimi/js-service-wrapper/blob/master/LICENSE)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbehnamazimi%2Fjs-service-wrapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbehnamazimi%2Fjs-service-wrapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbehnamazimi%2Fjs-service-wrapper/lists"}