Repository: causaly/fp-ts-timeout
Pull request #59: feat: introduce the `withTaskEitherRetry` and the `withReaderTaskEitherRetry` functions
Author: login: NikosTsompanides
Created: 2025-03-13T09:14:53Z | Merged: 2025-03-13T15:12:07Z

This PR introduces two new functions, `withTaskEitherRetry` and `withReaderTaskEitherRetry`, leveraging the [p-retry](https://www.npmjs.com/package/p-retry) package for retrying asynchronous operations.


Diff:
diff --git a/LICENSE b/LICENSE
index 06a5738..b12f109 100644
--- a/LICENSE
+++ b/LICENSE
@@ -7,3 +7,17 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 
 THE 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.
+
+---
+
+This project also includes third-party software under the MIT License:
+
+[p-retry](https://www.npmjs.com/package/p-retry)
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
+
+Permission 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:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE 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.
\ No newline at end of file
diff --git a/lib/index.ts b/lib/index.ts
index 52ccbf7..98783a9 100644
--- a/lib/index.ts
+++ b/lib/index.ts
@@ -1,3 +1,5 @@
 export * from './withTaskEitherTimeout.ts';
 export * from './withReaderTaskEitherTimeout.ts';
 export * from './errors.ts';
+export * from './withTaskEitherRetry.ts';
+export * from './withReaderTaskEitherRetry.ts';
diff --git a/lib/withReaderTaskEitherRetry.test.ts b/lib/withReaderTaskEitherRetry.test.ts
new file mode 100644
index 0000000..c2507bf
--- /dev/null
+++ b/lib/withReaderTaskEitherRetry.test.ts
@@ -0,0 +1,75 @@
+import { pipe } from 'fp-ts/lib/function.js';
+
+import * as TaskEither from 'fp-ts/lib/TaskEither.js';
+import {
+  expectLeftReaderTaskEither,
+  expectRightReaderTaskEither,
+} from 'jest-fp-ts-matchers';
+import { withReaderTaskEitherRetry } from './withReaderTaskEitherRetry.ts';
+
+describe('withTaskEitherTimeout()', () => {
+  it('should not retry the task if it succeeds', () => {
+    const retries = 2;
+    const run = vi.fn(() => Promise.resolve('success'));
+    const rte = () => TaskEither.fromTask(run);
+
+    return pipe(
+      rte,
+      withReaderTaskEitherRetry({
+        retries,
+        // speed up the tests
+        minTimeout: 1,
+        maxTimeout: 2,
+      }),
+      expectRightReaderTaskEither((value) => {
+        expect(run).toHaveBeenCalledTimes(1);
+        expect(value).toStrictEqual('success');
+      })
+    )({})();
+  });
+
+  it('should retry the task and succeed after max retries', () => {
+    const retries = 2;
+    const run = vi
+      .fn()
+      .mockRejectedValueOnce(new Error('failed'))
+      .mockRejectedValueOnce(new Error('failed'))
+      .mockResolvedValueOnce('success');
+
+    const rte = () => TaskEither.fromTask(run);
+
+    return pipe(
+      rte,
+      withReaderTaskEitherRetry({
+        retries,
+        // speed up the tests
+        minTimeout: 1,
+        maxTimeout: 2,
+      }),
+      expectRightReaderTaskEither((value) => {
+        expect(value).toStrictEqual('success');
+        expect(run).toHaveBeenCalledTimes(retries + 1);
+      })
+    )({})();
+  });
+
+  it('should retry the task and fail after max retries', () => {
+    const retries = 2;
+    const run = vi.fn(() => Promise.reject(new Error('test')));
+    const rte = () => TaskEither.fromTask(run);
+
+    return pipe(
+      rte,
+      withReaderTaskEitherRetry({
+        retries,
+        // speed up the tests
+        minTimeout: 1,
+        maxTimeout: 2,
+      }),
+      expectLeftReaderTaskEither((err) => {
+        expect(err).toBeInstanceOf(Error);
+        expect(run).toHaveBeenCalledTimes(retries + 1);
+      })
+    )({})();
+  });
+});
diff --git a/lib/withReaderTaskEitherRetry.ts b/lib/withReaderTaskEitherRetry.ts
new file mode 100644
index 0000000..3e98af0
--- /dev/null
+++ b/lib/withReaderTaskEitherRetry.ts
@@ -0,0 +1,11 @@
+import { withTaskEitherRetry } from './withTaskEitherRetry.ts';
+import type * as ReaderTaskEither from 'fp-ts/lib/ReaderTaskEither.js';
+import type { Options } from 'p-retry';
+
+export const withReaderTaskEitherRetry =
+  (options?: Options) =>
+  <DependencyType, LeftType, RightType>(
+    rte: ReaderTaskEither.ReaderTaskEither<DependencyType, LeftType, RightType>
+  ): ReaderTaskEither.ReaderTaskEither<DependencyType, LeftType, RightType> =>
+  (env: DependencyType) =>
+    withTaskEitherRetry(options)(rte(env));
diff --git a/lib/withTaskEitherRetry.test.ts b/lib/withTaskEitherRetry.test.ts
new file mode 100644
index 0000000..7c00fd1
--- /dev/null
+++ b/lib/withTaskEitherRetry.test.ts
@@ -0,0 +1,75 @@
+import { pipe } from 'fp-ts/lib/function.js';
+
+import * as TaskEither from 'fp-ts/lib/TaskEither.js';
+import {
+  expectLeftTaskEither,
+  expectRightTaskEither,
+} from 'jest-fp-ts-matchers';
+import { withTaskEitherRetry } from './withTaskEitherRetry.ts';
+
+describe('withTaskEitherTimeout()', () => {
+  it('should not retry the task if it succeeds', () => {
+    const retries = 2;
+    const run = vi.fn(() => Promise.resolve('success'));
+    const taskEither = TaskEither.fromTask(run);
+
+    return pipe(
+      taskEither,
+      withTaskEitherRetry({
+        retries,
+        // speed up the tests
+        minTimeout: 1,
+        maxTimeout: 2,
+      }),
+      expectRightTaskEither((value) => {
+        expect(run).toHaveBeenCalledTimes(1);
+        expect(value).toStrictEqual('success');
+      })
+    )();
+  });
+
+  it('should retry the task and succeed after max retries', () => {
+    const retries = 2;
+    const run = vi
+      .fn()
+      .mockRejectedValueOnce(new Error('failed'))
+      .mockRejectedValueOnce(new Error('failed'))
+      .mockResolvedValueOnce('success');
+
+    const taskEither = TaskEither.fromTask(run);
+
+    return pipe(
+      taskEither,
+      withTaskEitherRetry({
+        retries,
+        // speed up the tests
+        minTimeout: 1,
+        maxTimeout: 2,
+      }),
+      expectRightTaskEither((value) => {
+        expect(value).toStrictEqual('success');
+        expect(run).toHaveBeenCalledTimes(retries + 1);
+      })
+    )();
+  });
+
+  it('should retry the task and fail after max retries', () => {
+    const retries = 2;
+    const run = vi.fn(() => Promise.reject(new Error('test')));
+    const taskEither = TaskEither.fromTask(run);
+
+    return pipe(
+      taskEither,
+      withTaskEitherRetry({
+        retries,
+        // speed up the tests
+        minTimeout: 1,
+        maxTimeout: 2,
+      }),
+      expectLeftTaskEither((err) => {
+        expect(err).toBeInstanceOf(Error);
+        expect(run).toHaveBeenCalledTimes(retries + 1);
+      })
+    )();
+  });
+});
diff --git a/lib/withTaskEitherRetry.ts b/lib/withTaskEitherRetry.ts
new file mode 100644
index 0000000..2fd5283
--- /dev/null
+++ b/lib/withTaskEitherRetry.ts
@@ -0,0 +1,27 @@
+import pRetry from 'p-retry';
+import * as TaskEither from 'fp-ts/lib/TaskEither.js';
+import { identity, pipe } from 'fp-ts/lib/function.js';
+import type { Options } from 'p-retry';
+
+export const withTaskEitherRetry =
+  (options?: Options) =>
+  <LeftType, RightType>(
+    taskEither: TaskEither.TaskEither<LeftType, RightType>
+  ): TaskEither.TaskEither<LeftType, RightType> => {
+    // Convert task either to a throwable task
+    // to let p-retry catch the errors
+    const task = pipe(
+      taskEither,
+      TaskEither.match((err) => {
+        throw err;
+      }, identity)
+    );
+
+    return TaskEither.tryCatch(
+      () => pRetry(task, options),
+      // promises return unknown errors by default
+      // however we know the error type because we are using a TaskEither
+      // therefore, it's safe to cast the error to LeftType
+      (err) => err as LeftType
+    );
+  };
diff --git a/package-lock.json b/package-lock.json
index be46331..a73da6d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,6 +24,7 @@
         "husky": "^8.0.3",
         "jest-fp-ts-matchers": "^0.5.0",
         "lint-staged": "^15.0.1",
+        "p-retry": "^6.2.1",
         "prettier": "^2.8.8",
         "rimraf": "^5.0.1",
         "tsup": "^8.4.0",
@@ -1890,6 +1891,13 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@types/retry": {
+      "version": "0.12.2",
+      "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz",
+      "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/semver": {
       "version": "7.5.8",
       "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
@@ -5154,6 +5162,19 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-network-error": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz",
+      "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/is-number": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -6562,6 +6583,24 @@
         "node": ">=6"
       }
     },
+    "node_modules/p-retry": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz",
+      "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/retry": "0.12.2",
+        "is-network-error": "^1.0.0",
+        "retry": "^0.13.1"
+      },
+      "engines": {
+        "node": ">=16.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/p-try": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
@@ -7295,6 +7334,16 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/retry": {
+      "version": "0.13.1",
+      "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+      "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
     "node_modules/reusify": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
diff --git a/package.json b/package.json
index 86dbdce..9ba215c 100644
--- a/package.json
+++ b/package.json
@@ -80,6 +80,7 @@
     "husky": "^8.0.3",
     "jest-fp-ts-matchers": "^0.5.0",
     "lint-staged": "^15.0.1",
+    "p-retry": "^6.2.1",
     "prettier": "^2.8.8",
     "rimraf": "^5.0.1",
     "tsup": "^8.4.0",
diff --git a/tsup.config.ts b/tsup.config.ts
index ce5d1ad..3298d28 100644
--- a/tsup.config.ts
+++ b/tsup.config.ts
@@ -8,4 +8,5 @@ export default defineConfig({
   format: ['cjs', 'esm'],
   dts: true,
   outDir: 'dist',
+  noExternal: ['p-retry'],
 });


Reviews:
user: login: NikosTsompanides
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T10:04:16Z
user: login: jmike
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T12:41:12Z
user: login: jmike
body: Solid work Niko
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T13:08:13Z
user: login: jmike
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T13:11:11Z
user: login: jmike
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T13:12:03Z
user: login: NikosTsompanides
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T13:24:23Z
user: login: NikosTsompanides
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T13:25:02Z
user: login: NikosTsompanides
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T13:25:52Z
user: login: jmike
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T14:01:33Z

Review comments:
diff_hunk: @@ -8,4 +8,5 @@ export default defineConfig({
   format: ['cjs', 'esm'],
   dts: true,
   outDir: 'dist',
+  noExternal: ['p-retry'],
path: tsup.config.ts
user: login: NikosTsompanides
body: [dev]: reinclude `p-retry` package in the bundle.
created_at: 2025-03-13T10:04:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -88,5 +88,8 @@
   },
   "peerDependencies": {
     "fp-ts": "^2.16.9"
+  },
+  "dependencies": {
+    "p-retry": "^6.2.1"
path: package.json
user: login: jmike
body: suggestion:I think p-retry should be added under `devDependencies` given that it's bundled into the final distributable.
created_at: 2025-03-13T12:38:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,4 +8,5 @@ export default defineConfig({
   format: ['cjs', 'esm'],
   dts: true,
   outDir: 'dist',
+  noExternal: ['p-retry'],
path: tsup.config.ts
user: login: jmike
body: note: please make sure to retain the original license of `p-retry` in the distributable. We are legally obliged to do so.

Read https://github.com/sindresorhus/p-retry/blob/main/license for further details.
created_at: 2025-03-13T12:40:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -7,3 +7,13 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 
 THE 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.
+
+MIT License
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
+
+Permission 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:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE 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.
path: LICENSE
user: login: jmike
body: question: do you think we should store this in a separate license file? I've never done this before, so I don't know what the standard is.

cc/ @nikoskalogridis @thanoskrg 
created_at: 2025-03-13T13:04:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,24 @@
+import pRetry from 'p-retry';
+import * as TaskEither from 'fp-ts/lib/TaskEither.js';
+import { identity, pipe } from 'fp-ts/lib/function.js';
+import type { Options } from 'p-retry';
+
+export const withTaskEitherRetry =
+  (options?: Options) =>
+  <LeftType, RightType>(
+    taskEither: TaskEither.TaskEither<LeftType, RightType>
+  ): TaskEither.TaskEither<LeftType, RightType> => {
+    // Convert task either to a throwable task
+    // to let p-retry catch the errors
+    const task = pipe(
+      taskEither,
+      TaskEither.match((err) => {
+        throw err;
+      }, identity)
+    );
+
+    return TaskEither.tryCatch(
+      () => pRetry(task, options),
+      (err) => err as LeftType
path: lib/withTaskEitherRetry.ts
user: login: jmike
body: suggestion: add a comment above L22

```suggestion
      // promises return unknown errors by default
      // however we know the error type because we are using a TaskEither
      // therefore, it's safe to cast the error to LeftType
      (err) => err as LeftType
```
created_at: 2025-03-13T13:07:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -7,3 +7,13 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 
 THE 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.
+
+MIT License
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
+
+Permission 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:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE 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.
path: LICENSE
user: login: jmike
body: Our dear friend, chat-gtp proposes the following...
https://chatgpt.com/share/67d2d949-0ee4-800d-8f3b-6e510adb1e79
created_at: 2025-03-13T13:11:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -7,3 +7,13 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 
 THE 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.
+
+MIT License
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
+
+Permission 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:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE 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.
path: LICENSE
user: login: jmike
body: Let's follow this structure per the above suggestion...

```
MIT License

Copyright (c) [Year] [Your Name or Organization]

Permission 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:

[...Your MIT License continues here...]

---

This project also includes third-party software under the MIT License:

[Dependency Name]  
Copyright (c) [Year] [Dependency Author/Organization]  

Permission 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:

[Dependency’s MIT License continues here...]

```
created_at: 2025-03-13T13:11:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -7,3 +7,13 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 
 THE 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.
+
+MIT License
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
+
+Permission 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:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE 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.
path: LICENSE
user: login: NikosTsompanides
body: Dude. 
created_at: 2025-03-13T13:24:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -7,3 +7,13 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 
 THE 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.
+
+MIT License
+
+Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
+
+Permission 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:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE 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.
path: LICENSE
user: login: NikosTsompanides
body: Addressed with: [15e1f31](https://github.com/causaly/fp-ts-timeout/pull/59/commits/15e1f314278cf8b3ed8114359fff5b617d6a1413) ✅ 
created_at: 2025-03-13T13:25:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,24 @@
+import pRetry from 'p-retry';
+import * as TaskEither from 'fp-ts/lib/TaskEither.js';
+import { identity, pipe } from 'fp-ts/lib/function.js';
+import type { Options } from 'p-retry';
+
+export const withTaskEitherRetry =
+  (options?: Options) =>
+  <LeftType, RightType>(
+    taskEither: TaskEither.TaskEither<LeftType, RightType>
+  ): TaskEither.TaskEither<LeftType, RightType> => {
+    // Convert task either to a throwable task
+    // to let p-retry catch the errors
+    const task = pipe(
+      taskEither,
+      TaskEither.match((err) => {
+        throw err;
+      }, identity)
+    );
+
+    return TaskEither.tryCatch(
+      () => pRetry(task, options),
+      (err) => err as LeftType
path: lib/withTaskEitherRetry.ts
user: login: NikosTsompanides
body: Addressed with: [15e1f31](https://github.com/causaly/fp-ts-timeout/pull/59/commits/15e1f314278cf8b3ed8114359fff5b617d6a1413) ✅ 
created_at: 2025-03-13T13:25:52Z
author_association: CONTRIBUTOR

PR comments:
user: login: NikosTsompanides
created_at: 2025-03-13T13:40:25Z
body: @jmike @thanoskrg  After our offline discussion, I recommend proceeding with the current implementation to unblock our platform work. We can include p-retry in the bundle and release a minor version update for now. Later, we can convert the module to ESM and bump the version to a major update (i.e., 1.0.0).
author_association: CONTRIBUTOR
user: login: NikosTsompanides
created_at: 2025-03-13T15:11:37Z
body: I've symlinked the package locally to the platform, and everything is working as expected. All tests are passing as well. I'll proceed with merging the PR
author_association: CONTRIBUTOR