{"id":18318415,"url":"https://github.com/tether/promise-clerk","last_synced_at":"2025-06-22T08:38:09.648Z","repository":{"id":73071276,"uuid":"122264728","full_name":"tether/promise-clerk","owner":"tether","description":null,"archived":false,"fork":false,"pushed_at":"2018-02-21T15:53:00.000Z","size":88,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-09T14:08:51.763Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/tether.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-02-20T22:30:54.000Z","updated_at":"2018-02-20T22:55:47.000Z","dependencies_parsed_at":"2023-05-01T03:49:33.909Z","dependency_job_id":null,"html_url":"https://github.com/tether/promise-clerk","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/tether/promise-clerk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tether%2Fpromise-clerk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tether%2Fpromise-clerk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tether%2Fpromise-clerk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tether%2Fpromise-clerk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tether","download_url":"https://codeload.github.com/tether/promise-clerk/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tether%2Fpromise-clerk/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261263486,"owners_count":23132555,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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-05T18:09:31.310Z","updated_at":"2025-06-22T08:38:04.637Z","avatar_url":"https://github.com/tether.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Promise Clerk for Fault Tolerance\n\nA clerk is a white-collar worker who conducts general office tasks. The promise-clerk package conducts general Promise-related tasks to help with fault tolerance.\n\n[![Build Status](https://travis-ci.org/tether/promise-clerk.svg?branch=master)](https://travis-ci.org/tether/promise-clerk)\n[![NPM](https://img.shields.io/npm/v/promise-clerk.svg)](https://www.npmjs.com/package/promise-clerk)\n[![Downloads](https://img.shields.io/npm/dm/promise-clerk.svg)](http://npm-stat.com/charts.html?package=promise-clerk)\n[![guidelines](https://tether.github.io/contribution-guide/badge-guidelines.svg)](https://github.com/tether/contribution-guide)\n\n---\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*\n\n  - [Installing](#installing)\n  - [Overview](#overview)\n    - [`PromiseChainErrorCatcher`](#promisechainerrorcatcher)\n    - [`assertIsPromise`](#assertispromise)\n    - [`Quitter`](#quitter)\n    - [`ReallyDeterminedPropertyGetter`](#reallydeterminedpropertygetter)\n    - [`Transaction`](#transaction)\n- [Questions](#questions)\n- [Contribution](#contribution)\n  - [Testing](#testing)\n  - [Style Guide](#style-guide)\n- [Question](#question)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n### Installing\n\n```shell\nnpm install --save github:tether/promise-clerk#v1.0.0\n```\n\n### Overview\n* `PromiseChainErrorCatcher`: catch and log errors (with context) in a Promise chain \n* `assertIsPromise`: converts non-promise objects into rejected Promises, returns Promise objects as-is\n* `Quitter`: convenient way to break a Promise chain, especially when the chain was constructed with a loop\n* `ReallyDeterminedPropertyGetter`: if you can't trust a data source, provide one or more fallbacks\n* `Transaction`: Resume multi-step operations at the failed step\n\n#### `PromiseChainErrorCatcher`\nPromiseChainErrorCatcher can be used to collect and then summarize errors.\nIt is designed to simplify debugging complex Promise chains\n\n```js\n  import { PromiseChainErrorCatcher } from 'promise-clerk'\n  const errorCatcher = new PromiseChainErrorCatcher('Your Module')\n\n  const promiseUnderTest = riskyFooBar()\n    .catch(errorCatcher.nameError('Foo Bar step'))\n    .catch(error =\u003e {\n      errorCatcher.push(error)\n      return riskyQuux()\n        .then(errorCatcher.recordSuccess('Quux step'))\n        .catch(errorCatcher.nameError('Quux step'))\n    })  \n    .then(value =\u003e {\n      return doNotCareIfThisFailsOrSucceeds()\n        .catch(errorCatcher.nameError('Unimportant step'))\n        .catch(errorCatcher.catchError)\n        .then(() =\u003e doSomethingThatFails())\n        .catch(errorCatcher.nameError('Failing step'))\n    })  \n    .catch(errorCatcher.handleFinalError())\n```\nThis will reject with:\n\n```txt\nYour Module has failed.\n  - Foo Bar step failed because \u003cerror message from riskyFooBar\u003e\n  - Quux step resolved with \"value from riskyQuux\"\n  - Unimportant step failed because \u003cerror message from doNotCareIfThisFailsOrSucceeds\u003e\n  - Failing step failed because \u003cerror message from doSomethingThatFails\u003e\n```\n\nNote: this was recently extracted from the ReallyDeterminedPropertyGetter and may not be fully generalized yet. \n\n#### `assertIsPromise`\n```js\n  import { assertIsPromise } from 'promise-clerk'\n\n  assertIsPromise(probablyReturnsPromise())\n    .then(doSuffAfterPromise())\n\n```\nassertIsPromise always returns a Promise. If it was not given a Promise, it returns a rejected Promise, otherwise it returns the original Promise. It considers the argument a Promise if it's truthy and has a truthy property named `then`. \n\nNote, this was recently extracted from ReallyDeterminedPropertyGetter and PromiseChainErrorCatcher, and may not be fully generalized yet.\n\n#### `Quitter`\nQuitter is a convenient way to break a Promise chain, especially when the chain was constructed with a loop\n\n```js\n  import { Quitter } from 'promise-clerk'\n\n  const quitter = new Quitter()\n\n  const promise = Promise.resolve()\n\n  listOfEndpoints.forEach(endpoint =\u003e {\n    promise.then(value =\u003e {\n      quitter.maybeQuit(new Error('not continuing'))\n      return endpoint.getData()\n        .then(data =\u003e {\n          quitter.quitOnCondition(data.isReallyBad)\n          return data\n        })\n    })\n  })\n```\n\nIn this example, for each endpoint in the list, you `getData` -- but if any endpoint returns data that ` { isReallyBad: true }`, then you don't fetch the data from any more endpoints.\nYou could also use the `quit` method instead of `quitOnCondition` if there is no condition.\n\n#### `ReallyDeterminedPropertyGetter`\nReallyDeterminedPropertyGetter provides a way to define an external value (e.g. accessible via some API)\n  with one or more secondary data sources to fall back to in case the primary one is unavailable\n\n```js\n  import { ReallyDeterminedPropertyGetter } from 'promise-clerk' \n  const movieListing = await new ReallyDeterminedPropertyGetter()\n    .verify(movies =\u003e movies.length === movieTitles.length)\n    .primarySource(() =\u003e mainAPI.getMovieListingsPromise(movieTitles))\n    .secondarySource(() =\u003e Promise.all(movieTitles.map(title =\u003e otherAPI.getMovie(title))))\n    .secondarySource(() =\u003e new Promise((resolve, reject) =\u003e\n      oldSchoolHttpGet('https://movies.com?titles=' + movieTitles,join(','), (err, results) =\u003e err ? reject(err) : resolve(results))\n    ))\n    .synchronizeWithPrimarySource(movies =\u003e mainAPI.updateMovieListings(movies))\n    .ignoreSynchronizationErrors() // Only if you don't care whether mainApi.updateMovieListings() succeeds or fails\n    .get()\n```\n\n The basic algorithm is:\n\n - if no `verify` method is provided, consider all values verified. Otherwise, a value is considered verified if the `verify` method returns true when provided the value\n - try getting the value from a primary source, return it if found and verified\n - try getting value from a secondary source, return it if found and verified\n - repeat until the value is found and verified or we're out of sources\n\n Available methods are:\n\n - `primarySource(getter: Function\u003cPromise\u003e)`         // Required, may only be called once. Register a primary source\n - `secondarySource(getter: Function\u003cPromise\u003e)`       // Optional, may be called any number of times. Register a secondary source (will be attempted in the order added)\n - `verify(verify: (value) =\u003e boolean)`               // Optional, may only be called once. Will be called for each found value. Values for which `verify` returns false will be ignored\n - `synchronizeWithPrimarySource((value) =\u003e Promise)` // Optional, may be called any number of times. Registers a callback which is called if the primary source fails but a secondary source succeeds\n - `ignoreSynchronizationErrors()`                    // Optional. If it has been called, then any errors produced by a primarySourceSynchronizer function are ignored instead of causing the main `get` method to reject\n - `get()`                                            // Returns a Promise which resolves with the result, if available. May be called repeatedly as long as the returned Promise resolves before calling `get` again\n\nNote: this class uses the Builder Pattern (read more: https://en.wikipedia.org/wiki/Builder_pattern) to avoid having a long list of constructor arguments,\n some being optional, others required, etc..\n\n#### `Transaction`\n\nTransaction class provides a robust way to run a series of asynchronous functions in sequence\n\n```js\n  import { Transaction } from 'promise-clerk'\n  const transaction = new Transaction([\n    () =\u003e doThingOne(),\n    () =\u003e doThingTwo()\n    () =\u003e doThingThree()\n  ]).then(() =\u003e tellUserThatOperationSucceeded())\n\n  transaction.onResume(() =\u003e showLoadingIndicator())\n  transaction.onSpecificError('404', () =\u003e specialHandlingOf404Error())\n  transaction.onSpecificError(/5\\d\\d/, () =\u003e specialHandlingOf5xxError())\n  transaction.onError(error =\u003e {\n    tellUserThatSomethingWentWrong(error.message)\n      .then(userAnswer =\u003e {\n        if(userAnswer === 'try again') {\n          transaction.resume() // Repeats the failed step and continues with subsequent steps\n        }\n      })\n  })\n```\n\n## Questions\n\nFor support, bug reports and or feature requests please make sure to read our\n\u003ca href=\"https://github.com/tether/contribution-guide/blob/master/community.md\" target=\"_blank\"\u003ecommunity guidelines\u003c/a\u003e and use the issue list of this repo and make sure it's not present yet in our reporting checklist.\n\n## Contribution\nThe open source community is very important to us. If you want to participate to this repository, please make sure to read our \u003ca href=\"https://github.com/tether/contribution-guide\" target=\"_blank\"\u003eguidelines\u003c/a\u003e before making any pull request. If you have any related project, please let everyone know in our wiki.:1\n\nNeed inspiration for naming or design? Consider [Netflix's Hystrix fault-tolerance library](https://github.com/Netflix/Hystrix/)\n\n### Testing\n- Each utility should have a unit test named `utility-name.test.js` in the test folder.\n\n### Style Guide\n- We use [Standard JS](https://standardjs.com/) -- please run `npm run standard` and deal with any issues before committing.\n\n## Question\n\nFor support, bug reports and or feature requests please make sure to read our\n\u003ca href=\"https://github.com/tether/contribution-guide/blob/master/community.md\" target=\"_blank\"\u003ecommunity guidelines\u003c/a\u003e and use the issue list of this repo and make sure it's not present yet in our reporting checklist.\n\n## License\nThe MIT License (MIT)\n\nCopyright (c) 2018 Petrofeed Inc\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftether%2Fpromise-clerk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftether%2Fpromise-clerk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftether%2Fpromise-clerk/lists"}