{"id":25440246,"url":"https://github.com/diplomatiq/resily","last_synced_at":"2025-11-01T10:30:32.339Z","repository":{"id":35478136,"uuid":"217908159","full_name":"Diplomatiq/resily","owner":"Diplomatiq","description":"Resily is a TypeScript resilience and transient-fault-handling library that allows developers to express policies such as Retry, Fallback, Circuit Breaker, Timeout, Bulkhead Isolation, and Cache. Inspired by App-vNext/Polly.","archived":false,"fork":false,"pushed_at":"2023-01-16T04:01:31.000Z","size":828,"stargazers_count":7,"open_issues_count":22,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-11T10:04:29.269Z","etag":null,"topics":["bulkhead-isolation","cache","circuit-breaker","fallback","fault-handling","proactive-policies","reactive-policies","resilience","retry","timeout"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@diplomatiq/resily","language":"TypeScript","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/Diplomatiq.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null}},"created_at":"2019-10-27T19:52:06.000Z","updated_at":"2024-02-09T13:00:51.000Z","dependencies_parsed_at":"2023-01-16T22:29:13.666Z","dependency_job_id":null,"html_url":"https://github.com/Diplomatiq/resily","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Diplomatiq%2Fresily","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Diplomatiq%2Fresily/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Diplomatiq%2Fresily/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Diplomatiq%2Fresily/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Diplomatiq","download_url":"https://codeload.github.com/Diplomatiq/resily/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239281771,"owners_count":19612931,"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":["bulkhead-isolation","cache","circuit-breaker","fallback","fault-handling","proactive-policies","reactive-policies","resilience","retry","timeout"],"created_at":"2025-02-17T11:30:01.495Z","updated_at":"2025-11-01T10:30:32.294Z","avatar_url":"https://github.com/Diplomatiq.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"logo.png\" width=\"500px\"\u003e\n\u003c/p\u003e\n\nResily is a TypeScript resilience and transient-fault-handling library that allows developers to express policies such as Retry, Fallback, Circuit Breaker, Timeout, Bulkhead Isolation, and Cache. Inspired by [App-vNext/Polly](https://github.com/App-vNext/Polly).\n\n\u003cp\u003e\n\u003ca href=\"https://github.com/Diplomatiq/resily/actions?query=workflow%3ACI\" target=\"_blank\" style=\"text-decoration: none;\"\u003e\n  \u003cimg src=\"https://github.com/Diplomatiq/resily/workflows/CI/badge.svg\" alt=\"build status\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://github.com/Diplomatiq/resily\" target=\"_blank\" style=\"text-decoration: none;\"\u003e\n  \u003cimg src=\"https://img.shields.io/github/languages/top/Diplomatiq/resily.svg\" alt=\"languages used\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://www.npmjs.com/package/@diplomatiq/resily\" target=\"_blank\" style=\"text-decoration: none;\"\u003e\n  \u003cimg src=\"https://img.shields.io/npm/dt/@diplomatiq/resily.svg\" alt=\"downloads from npm\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://www.npmjs.com/package/@diplomatiq/resily\" target=\"_blank\" style=\"text-decoration: none;\"\u003e\n  \u003cimg src=\"https://img.shields.io/npm/v/@diplomatiq/resily.svg\" alt=\"latest released version on npm\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://github.com/Diplomatiq/resily/blob/main/LICENSE\" target=\"_blank\" style=\"text-decoration: none;\"\u003e\n  \u003cimg src=\"https://img.shields.io/npm/l/@diplomatiq/resily.svg\" alt=\"license\"\u003e\n\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## Installation\n\nBeing an npm package, you can install resily with the following command:\n\n```bash\nnpm install -P @diplomatiq/resily\n```\n\n## Testing\n\nRun tests with the following:\n\n```bash\nnpm test\n```\n\n## Usage\n\n_Note: This package is built as an ES6 package. You will not be able to use `require()`._\n\nAfter installation, you can import policies and other helper classes into your project, then wrap your code into one or more policies.\n\nEvery policy extends the abstract `Policy` class, which has an `execute` method. Your code wrapped into a policy gets executed when you invoke `execute`. The `execute` method is asynchronous, so it returns a `Promise` resolving with the return value of the executed method (or rejecting with an exception thrown by the method).\n\nThe wrapped method can be synchronous or asynchronous, it will be awaited in either case:\n\n```typescript\nasync function main() {\n    const policy = … // any policy\n\n    // configure the policy before executing code, see below\n\n    // then execute some code wrapped into the policy\n    // execute is async, so it returns a Promise\n    const result = await policy.execute(\n        // the wrapped method can be sync or async\n        async () =\u003e {\n            // the executed code\n            return 5;\n        },\n    );\n\n    // the value of result is 5\n}\n```\n\nSee concrete usage examples below at the policies' documentation.\n\n## Policies\n\nResily offers **reactive** and **proactive** policies:\n\n-   A **reactive** policy executes the wrapped method, then reacts to the outcome (which in practice is the result of or an exception thrown by the executed method) by acting as specified in the policy itself. Examples for reactive policies include retry, fallback, circuit-breaker.\n-   A **proactive** policy executes the wrapped method, then acts on its own as specified in the policy itself, regardless of the outcome of the executed code. Examples for proactive policies include timeout, bulkhead isolation, cache.\n\n#### Reactive policies summary\n\n| Policy                                            | What does it claim?                                                                                                                                                 | How does it work?                                                                                                                                     |\n| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [**RetryPolicy**](#retrypolicy)                   | Many faults are transient and will not occur again after a delay.                                                                                                   | Allows configuring automatic retries on specified conditions.                                                                                         |\n| [**FallbackPolicy**](#fallbackpolicy)             | Failures happen, and we can prepare for them.                                                                                                                       | Allows configuring substitute values or automated fallback actions.                                                                                   |\n| [**CircuitBreakerPolicy**](#circuitbreakerpolicy) | Systems faulting under heavy load can recover easier without even more load — in these cases it's better to fail fast than to keep callers on hold for a long time. | If there are more consecutive faulty responses than the configured number, it breaks the circuit (blocks the executions) for a specified time period. |\n\n#### Proactive policies summary\n\n| Policy                                                  | What does it claim?                                                                                                        | How does it work?                                                                                     |\n| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |\n| [**TimeoutPolicy**](#timeoutpolicy)                     | After some time, it is unlikely that the call will be successful.                                                          | Ensures the caller does not have to wait more than the specified timeout.                             |\n| [**BulkheadIsolationPolicy**](#bulkheadisolationpolicy) | Too many concurrent calls can overload a resource.                                                                         | Limits the number of concurrently executed actions as specified.                                      |\n| [**CachePolicy**](#cachepolicy)                         | Within a given time frame, a system may respond with the same answer, thus there is no need to actually perform the query. | Retrieves the response from a local cache within the time frame, after storing it on the first query. |\n\n#### Helpers and utilities summary\n\n| Policy                                      | What does it claim?                            | How does it work?                                                                                       |\n| ------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------- |\n| [**NopPolicy**](#noppolicy)                 | Does not claim anything.                       | Executes the wrapped method, and returns its result or throws its exceptions, without any intervention. |\n| [**PolicyCombination**](#policycombination) | Combining policies leads to better resilience. | Allows any policies to be combined together.                                                            |\n\n### Reactive policies\n\nEvery reactive policy extends the `ReactivePolicy` class, which means they can be configured with predicates to react on specific results and/or exceptions:\n\n```typescript\nconst policy = … // any reactive policy\n\n// if the executed code returns 5, the policy will react\npolicy.reactOnResult(r =\u003e r === 5);\n\n// will react\nawait policy.execute(() =\u003e 5);\n\n// will react\nawait policy.execute(async () =\u003e 5);\n\n// will not react\nawait policy.execute(() =\u003e 2);\n\n// will not react\nawait policy.execute(async () =\u003e 2);\n```\n\n```typescript\nconst policy = … // any reactive policy\n\n// if the executed code throws a ConcurrentAccessException, the policy will react\npolicy.reactOnException(e =\u003e e instanceof ConcurrentAccessException);\n\n// will react\nawait policy.execute(() =\u003e {\n    throw new ConcurrentAccessException();\n});\n\n// will react\nawait policy.execute(async () =\u003e {\n    throw new ConcurrentAccessException();\n});\n\n// will not react\nawait policy.execute(() =\u003e {\n    throw new OutOfRangeException();\n});\n\n// will not react\nawait policy.execute(async () =\u003e {\n    throw new OutOfRangeException();\n});\n```\n\nIf the policy is configured to react on multiple kinds of results or exceptions, it will react if any of them occurs:\n\n```typescript\nconst policy = … // any reactive policy\n\npolicy.reactOnResult(r =\u003e r === 5);\npolicy.reactOnResult(r =\u003e r === 7);\npolicy.reactOnException(e =\u003e e instanceof ConcurrentAccessException);\npolicy.reactOnException(e =\u003e e instanceof InvalidArgumentException);\n\n// will react\nawait policy.execute(() =\u003e 5);\n\n// will react\nawait policy.execute(async () =\u003e 5);\n\n// will react\nawait policy.execute(() =\u003e 7);\n\n// will react\nawait policy.execute(async () =\u003e 7);\n\n// will react\nawait policy.execute(() =\u003e {\n    throw new ConcurrentAccessException();\n});\n\n// will react\nawait policy.execute(async () =\u003e {\n    throw new ConcurrentAccessException();\n});\n\n// will react\nawait policy.execute(() =\u003e {\n    throw new InvalidArgumentException();\n});\n\n// will react\nawait policy.execute(async () =\u003e {\n    throw new InvalidArgumentException();\n});\n\n// will not react\nawait policy.execute(() =\u003e 2);\n\n// will not react\nawait policy.execute(async () =\u003e 2);\n\n// will not react\nawait policy.execute(() =\u003e {\n    throw new OutOfRangeException();\n});\n\n// will not react\nawait policy.execute(async () =\u003e {\n    throw new OutOfRangeException();\n});\n```\n\nYou can configure the policy to react on any result and/or to any exception:\n\n```typescript\nconst policy = … // any reactive policy\n\n// react on any result\npolicy.reactOnResult(() =\u003e true);\n\n// react on any exception\npolicy.reactOnException(() =\u003e true);\n```\n\n#### RetryPolicy\n\n`RetryPolicy` claims that many faults are transient and will not occur again after a delay. It allows configuring automatic retries on specified conditions.\n\nSince `RetryPolicy` is a reactive policy, you need to configure the policy to retry the execution on specific results or exceptions with `reactOnResult` and `reactOnException`. See the [Reactive policies](#reactive-policies) section for details.\n\nConfigure how many retries you need or retry forever:\n\n```typescript\nimport { RetryPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new RetryPolicy\u003cstring\u003e();\n\n// retry until the result/exception is reactive, but maximum 3 times\npolicy.retryCount(3);\n\n// this overwrites the previous value\npolicy.retryCount(5);\n\n// this also overwrites the previous value\n// this is the same as policy.retryCount(Number.POSITIVE_INFINITY)\npolicy.retryForever();\n```\n\nPerform certain actions before retrying:\n\n```typescript\nimport { RetryPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new RetryPolicy\u003cstring\u003e();\n\npolicy.onRetry(\n    // onRetryFns can be sync or async, they will be awaited\n    async (result, error, currentRetryCount) =\u003e {\n        // this code will be executed before the currentRetryCount-th retry occurs\n        // result is undefined if reacting upon a thrown error\n        // error is undefined if reacting upon a result\n    },\n);\n\n// you can set multiple onRetryFns, they will run sequentially\npolicy.onRetry(async () =\u003e {\n    // this will be awaited first\n});\npolicy.onRetry(async () =\u003e {\n    // then this will be awaited\n});\n```\n\nWait for the specified number of milliseconds before retrying:\n\n```typescript\nimport { RetryPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new RetryPolicy\u003cstring\u003e();\n\n// wait for 100 ms before each retry\npolicy.waitBeforeRetry(() =\u003e 100);\n\n// this overwrites the previous backoff strategy\n// wait for 100 ms before the first retry, 200 ms before the second retry, etc.\npolicy.waitBeforeRetry((currentRetryCount) =\u003e currentRetryCount * 100);\n```\n\nThe waiting happens _before_ the execution of onRetryFns.\n\nAlthough you can code any kind of backoff, there are also predefined, ready-to-use backoff strategies:\n\n```typescript\nimport { BackoffStrategyFactory, RetryPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new RetryPolicy\u003cstring\u003e();\n\n// wait for 100 ms before each retry\n// 100 100 100 100 100 …\npolicy.waitBeforeRetry(BackoffStrategyFactory.constantBackoff(100));\n\n// retry immediately for the first time, then wait for 100 ms before each retry\n// 0 100 100 100 100 …\npolicy.waitBeforeRetry(BackoffStrategyFactory.constantBackoff(100, true));\n\n// wait for (currentRetryCount * 100) ms before each retry\n// 100 200 300 400 500 …\npolicy.waitBeforeRetry(BackoffStrategyFactory.linearBackoff(100));\n\n// retry immediately for the first time, then wait for ((currentRetryCount - 1) * 100) ms before each retry\n// 0 100 200 300 400 …\npolicy.waitBeforeRetry(BackoffStrategyFactory.linearBackoff(100, true));\n\n// wait for (100 * 2 ** (currentRetryCount - 1)) ms before each retry\n// 100 200 400 800 1600 …\npolicy.waitBeforeRetry(BackoffStrategyFactory.exponentialBackoff(100));\n\n// retry immediately for the first time, then wait for (100 * 2 ** (currentRetryCount - 2)) ms before each retry\n// 0 100 200 400 800 …\npolicy.waitBeforeRetry(BackoffStrategyFactory.exponentialBackoff(100, true));\n\n// wait for (100 * 3 ** (currentRetryCount - 1)) ms before each retry\n// 100 300 900 2700 8100 …\npolicy.waitBeforeRetry(BackoffStrategyFactory.exponentialBackoff(100, false, 3));\n\n// retry immediately for the first time, then wait for (100 * 3 ** (currentRetryCount - 2)) ms before each retry\n// 0 100 300 900 2700 …\npolicy.waitBeforeRetry(BackoffStrategyFactory.exponentialBackoff(100, true, 3));\n\n// wait for a [random between 1-100, inclusive] ms before each retry\npolicy.waitBeforeRetry(BackoffStrategyFactory.jitteredBackoff(1, 100));\n\n// retry immediately for the first time, then wait for a [random between 1-100, inclusive] ms before each retry\npolicy.waitBeforeRetry(BackoffStrategyFactory.jitteredBackoff(1, 100, true));\n```\n\nFor using `jitteredBackoff` in Node.js environments, you will need to inject a Node.js-based entropy source into the default RandomGenerator ([@diplomatiq/crypto-random](https://github.com/Diplomatiq/crypto-random) requires `window.crypto.getRandomValues` to be available by default). Create the following in your project:\n\n```typescript\nimport { EntropyProvider, UnsignedTypedArray } from '@diplomatiq/crypto-random';\nimport { randomFill } from 'crypto';\n\nexport class NodeJsEntropyProvider implements EntropyProvider {\n    public async getRandomValues\u003cT extends UnsignedTypedArray\u003e(array: T): Promise\u003cT\u003e {\n        return new Promise\u003cT\u003e((resolve, reject): void =\u003e {\n            randomFill(array, (error: Error | null, array: T): void =\u003e {\n                if (error !== null) {\n                    reject(error);\n                    return;\n                }\n                resolve(array);\n            });\n        });\n    }\n}\n```\n\nThen use it as follows:\n\n```typescript\nimport { RandomGenerator } from '@diplomatiq/crypto-random';\nimport { NodeJsEntropyProvider } from './nodeJsEntropyProvider';\n\nconst entropyProvider = new NodeJsEntropyProvider();\nconst randomGenerator = new RandomGenerator(entropyProvider);\n\nconst jitteredBackoff = BackoffStrategyFactory.jitteredBackoff(1, 100, true, randomGenerator);\n```\n\nPerform certain actions after the execution and all retries finished:\n\n```typescript\nimport { RetryPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new RetryPolicy\u003cstring\u003e();\n\npolicy.onFinally(\n    // onFinallyFns can be sync or async, they will be awaited\n    async () =\u003e {},\n);\n\n// you can set multiple onFinallyFns, they will run sequentially\npolicy.onFinally(async () =\u003e {\n    // this will be awaited first\n});\npolicy.onFinally(async () =\u003e {\n    // then this will be awaited\n});\n```\n\n#### FallbackPolicy\n\n`FallbackPolicy` claims that failures happen, and we can prepare for them. It allows configuring substitute values or automated fallback actions.\n\nSince `FallbackPolicy` is a reactive policy, you need to configure the policy to fallback along its fallback chain on specific results or exceptions with `reactOnResult` and `reactOnException`. See the [Reactive policies](#reactive-policies) section for details.\n\nConfigure the fallback chain:\n\n```typescript\nimport { FallbackPolicy } from '@diplomatiq/resily';\n\n// the wrapped method and its fallbacks are supposed to return a string\nconst policy = new FallbackPolicy\u003cstring\u003e();\n\n// if the wrapped method's result/exception is reactive, configure a fallback method onto the fallback chain\npolicy.fallback(\n    // the fallback methods can be sync or async, they will be awaited\n    () =\u003e {\n        // do something\n    },\n);\n\n// if the previous fallback method's result/exception is reactive, configure another fallback onto the fallback chain\npolicy.fallback(\n    // the fallback methods can be sync or async, they will be awaited\n    async () =\u003e {\n        // do something\n    },\n);\n\n// you can configure any number of fallback methods onto the fallback chain\n```\n\nIf there are no more elements on the fallback chain but the last result/exception is still reactive — meaning there are no more fallbacks when needed —, a `FallbackChainExhaustedException` is thrown.\n\nPerform certain actions before the fallback:\n\n```typescript\nimport { FallbackPolicy } from '@diplomatiq/resily';\n\n// the wrapped method and its fallbacks are supposed to return a string\nconst policy = new FallbackPolicy\u003cstring\u003e();\n\npolicy.onFallback(\n    // onFallbackFns can be sync or async, they will be awaited\n    async (result, error) =\u003e {\n        // result is undefined if reacting upon a thrown error\n        // error is undefined if reacting upon a result\n    },\n);\n\n// you can set multiple onFallbackFns, they will run sequentially\npolicy.onFallback(async () =\u003e {\n    // this will be awaited first\n});\npolicy.onFallback(async () =\u003e {\n    // then this will be awaited\n});\n```\n\nPerform certain actions after the execution and all fallbacks finished:\n\n```typescript\nimport { FallbackPolicy } from '@diplomatiq/resily';\n\n// the wrapped method and its fallbacks are supposed to return a string\nconst policy = new FallbackPolicy\u003cstring\u003e();\n\npolicy.onFinally(\n    // onFinallyFns can be sync or async, they will be awaited\n    async () =\u003e {},\n);\n\n// you can set multiple onFinallyFns, they will run sequentially\npolicy.onFinally(async () =\u003e {\n    // this will be awaited first\n});\npolicy.onFinally(async () =\u003e {\n    // then this will be awaited\n});\n```\n\n#### CircuitBreakerPolicy\n\n`CircuitBreakerPolicy` claims that systems faulting under heavy load can recover easier without even more load — in these cases it's better to fail fast than to keep callers on hold for a long time.\n\nIf there are more consecutive faulty responses than the configured number, it breaks the circuit (blocks the executions) for a specified time period.\n\nSince `CircuitBreakerPolicy` is a reactive policy, you need to configure the policy to break the circuit on specific results or exceptions with `reactOnResult` and `reactOnException`. See the [Reactive policies](#reactive-policies) section for details.\n\nThe `CircuitBreakerPolicy` has 4 states, and works as follows:\n\n`Closed`\n\n-   This is the initial state.\n-   When closed, the circuit allows executions, while measuring reactive results and exceptions. All results (reactive or not) are returned and all exceptions (reactive or not) are rethrown.\n-   When encountering altogether `numberOfConsecutiveReactionsBeforeCircuitBreak` reactive results or exceptions _consecutively_, the circuit transitions to `Open` state, meaning the circuit is broken.\n\n`Open`\n\n-   While the circuit is in `Open` state, no action wrapped into the policy gets executed. Every call will fail fast with a `BrokenCircuitException`.\n-   The circuit remains open for the specified duration. After the duration elapses, the subsequent execution call transitions the circuit to `AttemptingClose` state.\n\n`AttemptingClose`\n\n-   As the name implies, this state is an attempt to close the circuit.\n-   This is a temporary state of the circuit, existing only between the subsequent execution call to the circuit after the break duration elapsed in `Open` state, and the actual execution of the wrapped method.\n-   The next circuit state is determined by the result or exception produced by the executed method.\n\n    -   If the result or exception is reactive to the policy, the circuit transitions back to `Open` state for the specified circuit break duration.\n    -   If the result or exception is not reactive to the policy, the circuit transitions to `Closed` state.\n\n`Isolated`\n\n-   You can manually break the circuit by calling `policy.isolate()`, from any state. This transitions the circuit to `Isolated` state.\n-   While the circuit is in `Isolated` state, no action wrapped into the policy gets executed. Every call will fail fast with an `IsolatedCircuitException`.\n-   The circuit remains in `Isolated` state until `policy.reset()` is called.\n\nConfigure how many consecutive reactions should break the circuit:\n\n```typescript\nimport { CircuitBreakerPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new CircuitBreakerPolicy\u003cstring\u003e();\n\n// break the circuit after encountering 3 reactive results/exceptions consecutively\npolicy.breakAfter(3);\n\n// this overwrites the previous value\npolicy.breakAfter(5);\n```\n\nConfigure how long the circuit should be broken:\n\n```typescript\nimport { CircuitBreakerPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new CircuitBreakerPolicy\u003cstring\u003e();\n\n// break the circuit for 5000 ms\npolicy.breakFor(5000);\n\n// this overwrites the previous value\npolicy.breakFor(20000);\n```\n\nManage the circuit manually:\n\n```typescript\nimport { CircuitBreakerPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new CircuitBreakerPolicy\u003cstring\u003e();\n\n// break the circuit manually - it will be open indefinitely\nawait policy.isolate();\n\n// get the circuit's current state\nconst state = policy.getCircuitState();\n// 'Closed' | 'Open' | 'AttemptingClose' | 'Isolated'\n\n// reset the circuit after isolating - it will close\nif (state === 'Isolated') {\n    await policy.reset();\n}\n```\n\nPerform actions on state transitions:\n\n```typescript\nimport { CircuitBreakerPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new CircuitBreakerPolicy\u003cstring\u003e();\n```\n\n```typescript\npolicy.onClose(\n    // onCloseFns can be sync or async, they will be awaited\n    async () =\u003e {},\n);\n\n// you can set multiple onCloseFns, they will run sequentially\npolicy.onClose(async () =\u003e {\n    // this will be awaited first\n});\npolicy.onClose(async () =\u003e {\n    // then this will be awaited\n});\n```\n\n```typescript\npolicy.onOpen(\n    // onOpenFns can be sync or async, they will be awaited\n    async () =\u003e {},\n);\n\n// you can set multiple onOpenFns, they will run sequentially\npolicy.onOpen(async () =\u003e {\n    // this will be awaited first\n});\npolicy.onOpen(async () =\u003e {\n    // then this will be awaited\n});\n```\n\n```typescript\npolicy.onAttemptingClose(\n    // onAttemptingCloseFns can be sync or async, they will be awaited\n    async () =\u003e {},\n);\n\n// you can set multiple onAttemptingCloseFns, they will run sequentially\npolicy.onAttemptingClose(async () =\u003e {\n    // this will be awaited first\n});\npolicy.onAttemptingClose(async () =\u003e {\n    // then this will be awaited\n});\n```\n\n```typescript\npolicy.onIsolate(\n    // onIsolateFns can be sync or async, they will be awaited\n    async () =\u003e {},\n);\n\n// you can set multiple onIsolateFns, they will run sequentially\npolicy.onIsolate(async () =\u003e {\n    // this will be awaited first\n});\npolicy.onIsolate(async () =\u003e {\n    // then this will be awaited\n});\n```\n\n### Proactive policies\n\nEvery proactive policy extends the `ProactivePolicy` class.\n\n#### TimeoutPolicy\n\n`TimeoutPolicy` claims that after some time, it is unlikely the call will be successful. It ensures the caller does not have to wait more than the specified timeout.\n\nOnly asynchronous methods can be executed within a `TimeoutPolicy`, or else no timeout happens. `TimeoutPolicy` is implemented with `Promise.race()`, racing the promise returned by the executed method (`executionPromise`) with a promise that is rejected after the specified time elapses (`timeoutPromise`). If the executed method is not asynchronous (i.e. it does not have at least one point to pause its execution at), no timeout will happen even if the execution takes longer than the specified timeout duration, since there is no point in time for taking the control out from the executed method's hands to reject the `timeoutPromise`.\n\nThe executed method is fully executed to its end (unless it throws an exception), regardless of whether a timeout has occured or not. `TimeoutPolicy` ensures that the caller does not have to wait more than the specified timeout, but it does neither cancel nor abort\\* the execution of the method. This means that if the executed method has side effects, these side effects can occur even after the timeout happened.\n\n\\*TypeScript/JavaScript has no _generic_ way of canceling or aborting an executing method, either synchronous or asynchronous. `TimeoutPolicy` runs arbitrary user-provided code: it cannot be assumed the code is prepared in any way (e.g. it has cancel points). The provided code _could_ be executed in a separate worker thread so it can be aborted instantaneously by terminating the worker, but run-time compiling a worker from user-provided code is ugly and error-prone.\n\nOn timeout, the promise returned by the policy's `execute` method is rejected with a `TimeoutException`:\n\n```typescript\nimport { TimeoutException, TimeoutPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new TimeoutPolicy\u003cstring\u003e();\n\ntry {\n    const result = await policy.execute(async () =\u003e {\n        // the executed code\n    });\n} catch (ex) {\n    if (ex instanceof TimeoutException) {\n        // the operation timed out\n    } else {\n        // the executed method thrown an exception\n    }\n}\n```\n\nConfigure how long the waiting period should be:\n\n```typescript\nimport { TimeoutPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new TimeoutPolicy\u003cstring\u003e();\npolicy.timeoutAfter(1000); // timeout after 1000 ms\n```\n\nPerform certain actions on timeout:\n\n```typescript\nimport { TimeoutPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new TimeoutPolicy\u003cstring\u003e();\npolicy.onTimeout(\n    // onTimeoutFns can be sync or async, they will be awaited\n    async (timedOutAfterMs) =\u003e {\n        // the policy was configured to timeout after timedOutAfterMs\n    },\n);\n\n// you can set multiple onTimeoutFns, they will run sequentially\npolicy.onTimeout(async () =\u003e {\n    // this will be awaited first\n});\npolicy.onTimeout(async () =\u003e {\n    // then this will be awaited\n});\n```\n\nThrowing a `TimeoutException` from the executed method is not a timeout, therefore it does not trigger running `onTimeout` functions:\n\n```typescript\nimport { TimeoutException, TimeoutPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new TimeoutPolicy\u003cstring\u003e();\n\nlet onTimeoutRan = false;\npolicy.onTimeout(() =\u003e {\n    onTimeoutRan = true;\n});\n\ntry {\n    await policy.execute(async () =\u003e {\n        throw new TimeoutException();\n    });\n} catch (ex) {\n    // ex is a TimeoutException (thrown by the executed method)\n    const isTimeoutException = ex instanceof TimeoutException; // true\n}\n\n// onTimeoutRan is false\n```\n\n#### BulkheadIsolationPolicy\n\n`BulkheadIsolationPolicy` claims that too many concurrent calls can overload a resource. It limits the number of concurrently executed actions as specified.\n\nMethod calls executed via the policy are placed into a size-limited bulkhead compartment, limiting the maximum number of concurrent executions.\n\nIf the bulkhead compartment is full — meaning the maximum number of concurrent executions is reached —, additional calls can be queued up, ready to be executed whenever a place falls vacant in the bulkhead compartment (i.e. an execution finishes). Queuing up these calls ensures that the resource protected by the policy is always at maximum utilization, while limiting the number of concurrent actions ensures that the resource is not overloaded. The queue is a simple FIFO buffer.\n\nWhen the policy's `execute` method is invoked with a method to be executed, the policy's operation can be described as follows:\n\n-   `(1)` If there is an execution slot available in the bulkhead compartment, execute the method immediately.\n\n-   `(2)` Else if there is still space in the queue, enqueue the execution intent of the method — without actually executing the method —, then wait asynchronously until the method can be executed.\n\n    An execution intent gets dequeued — and its corresponding method gets executed — each time an execution slot becomes available in the bulkhead compartment.\n\n-   `(3)` Else throw a `BulkheadCompartmentRejectedException`.\n\nFrom the caller's point of view, this is all transparent: the promise returned by the `execute` method is\n\n-   either eventually resolved with the return value of the wrapped method (cases `(1)` and `(2)`),\n-   or eventually rejected with an exception thrown by the wrapped method (cases `(1)` and `(2)`),\n-   or rejected with a `BulkheadCompartmentRejectedException` (case `(3)`).\n\nConfigure the size of the bulkhead compartment:\n\n```typescript\nimport { BulkheadIsolationPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new BulkheadIsolationPolicy\u003cstring\u003e();\n\n// allow maximum 3 concurrent executions\npolicy.maxConcurrency(3);\n\n// this overwrites the previous value\npolicy.maxConcurrency(5);\n```\n\nConfigure the size of the queue:\n\n```typescript\nimport { BulkheadIsolationPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new BulkheadIsolationPolicy\u003cstring\u003e();\n\n// allow maximum 3 queued actions\npolicy.maxQueuedActions(3);\n\n// this overwrites the previous value\npolicy.maxQueuedActions(5);\n```\n\nGet usage information about the bulkhead compartment:\n\n```typescript\nimport { BulkheadIsolationPolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new BulkheadIsolationPolicy\u003cstring\u003e();\n\n// the number of available (free) execution slots in the bulkhead compartment\npolicy.getAvailableSlotsCount();\n\n// the number of available (free) spaces in the queue\npolicy.getAvailableQueuedActionsCount();\n```\n\n#### CachePolicy\n\n`CachePolicy` claims that within a given time frame, a system may respond with the same answer, thus there is no need to actually perform the query. It retrieves the response from a local cache within the time frame, after storing it on the first query.\n\nThe `CachePolicy` is implemented as a simple in-memory cache. It works as follows:\n\n-   For the first time (and every further time the cache is invalid), the `CachePolicy` executes the wrapped method, and caches its result.\n-   For subsequent execution calls, the cached result is returned and the wrapped method is not executed — as long as the cache remains valid.\n-   The cache is valid as long as it is not expired (see time to live settings below) or manually invalidated.\n\nConfigure how long the cache should be valid:\n\n```typescript\nimport { CachePolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new CachePolicy\u003cstring\u003e;\n\n// the cache is valid for 10000ms from the moment the value is stored in the cache\npolicy.timeToLive('relative', 10000);\n\n// the cache is valid as long as Date.now() \u003c 772149600000\n// this overwrites the previous setting\npolicy.timeToLive('absolute', 772149600000);\n\n// the cache is valid for 10000ms from the moment the value is stored in or retrieved from the cache\n// this overwrites the previous setting\npolicy.timeToLive('sliding', 10000);\n```\n\nInvalidate the cache manually, causing the next `execute` call to run the wrapped method:\n\n```typescript\nimport { CachePolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new CachePolicy\u003cstring\u003e;\n\npolicy.invalidate();\n```\n\nPerform actions on caching events:\n\n```typescript\nimport { CachePolicy } from '@diplomatiq/resily';\n\n// the wrapped method is supposed to return a string\nconst policy = new CachePolicy\u003cstring\u003e;\n```\n\n```typescript\n// perform an action before the value is retrieved from the cache\npolicy.onCacheGet(\n    // onCacheGetFns can be sync or async, they will be awaited\n    async () =\u003e {},\n);\n\n// you can set multiple onCacheGetFns, they will run sequentially\npolicy.onCacheGet(async () =\u003e {\n    // this will be awaited first\n});\npolicy.onCacheGet(async () =\u003e {\n    // then this will be awaited\n});\n```\n\n```typescript\n// perform an action before the wrapped method is executed and its result is cached\npolicy.onCacheMiss(\n    // onCacheMissFns can be sync or async, they will be awaited\n    async () =\u003e {},\n);\n\n// you can set multiple onCacheMissFns, they will run sequentially\npolicy.onCacheMiss(async () =\u003e {\n    // this will be awaited first\n});\npolicy.onCacheMiss(async () =\u003e {\n    // then this will be awaited\n});\n```\n\n```typescript\n// perform an action after the wrapped method is executed and its result is cached\npolicy.onCachePut(\n    // onCachePutFns can be sync or async, they will be awaited\n    async () =\u003e {},\n);\n\n// you can set multiple onCachePutFns, they will run sequentially\npolicy.onCachePut(async () =\u003e {\n    // this will be awaited first\n});\npolicy.onCachePut(async () =\u003e {\n    // then this will be awaited\n});\n```\n\n### Helpers and utilities\n\n#### NopPolicy\n\n`NopPolicy` does not claim anything. It executes the wrapped method, and returns its result or throws its exceptions, without any intervention.\n\n#### PolicyCombination\n\nPolicies can be combined in multiple ways. Given the following:\n\n```typescript\nimport { FallbackPolicy, RetryPolicy, TimeoutPolicy } from '@diplomatiq/resily';\n\nconst timeoutPolicy = new TimeoutPolicy();\nconst retryPolicy = new RetryPolicy();\nconst fallbackPolicy = new FallbackPolicy();\n\nconst fn = () =\u003e {\n    // the executed code\n};\n```\n\nThe naïve way to combine the above policies would be:\n\n```typescript\nfallbackPolicy.execute(() =\u003e retryPolicy.execute(() =\u003e timeoutPolicy.execute(fn)));\n```\n\nThe previous example is equivalent to the following:\n\n```typescript\nfallbackPolicy.wrap(retryPolicy);\nretryPolicy.wrap(timeoutPolicy);\n\nfallbackPolicy.execute(fn);\n```\n\nAnd also equivalent to the following:\n\n```typescript\nPolicyCombination.wrap([fallbackPolicy, retryPolicy, timeoutPolicy]).execute(fn);\n```\n\nPolicyCombination expects at least two policies to be combined.\n\n### Modifying a policy's configuration\n\nAll policies' configuration parameters are set via setter methods. This could imply that all policies can be safely reconfigured whenever needed, but providing setter methods instead of constructor parameters is merely because this way the policies are more convenient to use. If you need to reconfigure a policy, you can do that, but not while it is still executing one or more methods: reconfiguring while executing could lead to unexpected side-effects. Therefore, if you tries to reconfigure a policy while executing, a `PolicyModificationNotAllowedException` is thrown.\n\nTo safely reconfigure a policy, check whether it is executing or not:\n\n```typescript\nconst policy = … // any policy\n\nif (!policy.isExecuting()) {\n    // you can reconfigure the policy\n}\n```\n\n## Development\n\nSee [CONTRIBUTING.md](https://github.com/Diplomatiq/resily/blob/main/CONTRIBUTING.md) for details.\n\n---\n\nCopyright (c) 2018 Diplomatiq\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiplomatiq%2Fresily","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdiplomatiq%2Fresily","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiplomatiq%2Fresily/lists"}