@infra-blocks/retry 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # ts-retry
2
+ [![Build](https://github.com/infra-blocks/ts-retry/actions/workflows/build.yml/badge.svg)](https://github.com/infra-blocks/ts-retry/actions/workflows/build.yml)
3
+ [![Release](https://github.com/infra-blocks/ts-retry/actions/workflows/release.yml/badge.svg)](https://github.com/infra-blocks/ts-retry/actions/workflows/release.yml)
4
+ [![Update From Template](https://github.com/infra-blocks/ts-retry/actions/workflows/update-from-template.yml/badge.svg)](https://github.com/infra-blocks/ts-retry/actions/workflows/update-from-template.yml)
5
+
6
+ This repository exports generic retry utilities. The main function returns a `PromiseLike` object that also has a small `Emitter` API, allowing client code to
7
+ be notified every time their function is attempted.
@@ -0,0 +1,84 @@
1
+ import { EmitterLike, Predicate } from "@infra-blocks/types";
2
+ /**
3
+ * Default retry configuration.
4
+ *
5
+ * The default configuration retries 60 times, with a factor of 1 and a minimum interval of 1000ms.
6
+ * The maximum interval is set to Infinity, meaning that there is no upper bound on the wait time.
7
+ *
8
+ * When no field is overridden, this config will result in retrying the function every second
9
+ * for a minute. Note that retries don't include the first attempt, so the inner function is
10
+ * called 61 times in total.
11
+ */
12
+ export declare const DEFAULT_RETRY_CONFIG: Required<RetryConfig<unknown>>;
13
+ /**
14
+ * Configuration for the retry function.
15
+ */
16
+ export interface RetryConfig<E = Error> {
17
+ /**
18
+ * The amount of retries that will be attempted. Note that the first attempt
19
+ * doest not count as a retry. In total, the amount of retries plus one
20
+ * attempts will be made, in the worst case scenario.
21
+ */
22
+ retries?: number;
23
+ /**
24
+ * The exponential backoff factor that will be used between retries.
25
+ * The actual wait time can be calculated as such:
26
+ * wait time = min((factor ^ retry) * minIntervalMs, maxIntervalMs)
27
+ */
28
+ factor?: number;
29
+ /**
30
+ * The minimum wait time, in milliseconds, before the first retry.
31
+ * When the factor is 1, the wait time between retries is constant and
32
+ * equal to this value.
33
+ */
34
+ minIntervalMs?: number;
35
+ /**
36
+ * The maximum wait time between retries can be bound using this option.
37
+ */
38
+ maxIntervalMs?: number;
39
+ /**
40
+ * A predicate used to determine if an error should warrant a retry or not.
41
+ *
42
+ * When the function returns true, a retry will be attempted. When it returns
43
+ * false, the process immediately fails and throws the error.
44
+ *
45
+ * @param err - The error received from the inner function.
46
+ * @returns True if the function should be retried, false otherwise.
47
+ */
48
+ isRetryableError?: Predicate<E>;
49
+ }
50
+ /**
51
+ * Events and their handler types for the {@link Retry}.
52
+ */
53
+ export type RetryEvents<E = Error> = {
54
+ /**
55
+ * This event is emitted at the beginning of every attempt.
56
+ *
57
+ * @param attempt - The attempt number. Starts with 1, and the first attempt does not count as a retry.
58
+ */
59
+ attempt: (params: {
60
+ attempt: number;
61
+ retryConfig: Required<RetryConfig<E>>;
62
+ }) => void;
63
+ };
64
+ /**
65
+ * Type returned by the {@link retry} function.
66
+ *
67
+ * This type is both a promise and an event emitter, where the events are described as in
68
+ * {@link RetryEvents}.
69
+ */
70
+ export interface Retry<T, E = Error> extends EmitterLike<RetryEvents<E>>, PromiseLike<T> {
71
+ }
72
+ /**
73
+ * The type of the inner function that is wrapped by the {@link retry} function.
74
+ */
75
+ export type RetryFunction<R> = () => Promise<R>;
76
+ /**
77
+ * Returns a {@link Retry} object that wraps the provided function.
78
+ *
79
+ * @param fn - The inner function that will be retried.
80
+ * @param options - The configuration for the retry process, such as defined in {@link RetryConfig}.
81
+ *
82
+ * @returns A {@link Retry} that is configured with the provided arguments.
83
+ */
84
+ export declare function retry<T, E = Error>(fn: RetryFunction<T>, options?: RetryConfig<E>): Retry<T, E>;
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.retry = exports.DEFAULT_RETRY_CONFIG = void 0;
4
+ const promiseRetry = require("promise-retry");
5
+ const types_1 = require("@infra-blocks/types");
6
+ /**
7
+ * Default retry configuration.
8
+ *
9
+ * The default configuration retries 60 times, with a factor of 1 and a minimum interval of 1000ms.
10
+ * The maximum interval is set to Infinity, meaning that there is no upper bound on the wait time.
11
+ *
12
+ * When no field is overridden, this config will result in retrying the function every second
13
+ * for a minute. Note that retries don't include the first attempt, so the inner function is
14
+ * called 61 times in total.
15
+ */
16
+ exports.DEFAULT_RETRY_CONFIG = {
17
+ retries: 60,
18
+ factor: 1,
19
+ minIntervalMs: 1000,
20
+ maxIntervalMs: Infinity,
21
+ isRetryableError: () => true,
22
+ };
23
+ class RetryImpl extends types_1.EmitterLikeBase {
24
+ promise;
25
+ retryConfig;
26
+ constructor(fn, options) {
27
+ super();
28
+ this.retryConfig = { ...exports.DEFAULT_RETRY_CONFIG, ...options };
29
+ const wrapper = (attempt) => {
30
+ this.emit("attempt", {
31
+ attempt,
32
+ retryConfig: { ...this.retryConfig },
33
+ });
34
+ return fn();
35
+ };
36
+ this.promise = retryPromise(wrapper, this.retryConfig);
37
+ }
38
+ then(onfulfilled, onrejected) {
39
+ return this.promise.then(onfulfilled, onrejected);
40
+ }
41
+ }
42
+ /**
43
+ * Returns a {@link Retry} object that wraps the provided function.
44
+ *
45
+ * @param fn - The inner function that will be retried.
46
+ * @param options - The configuration for the retry process, such as defined in {@link RetryConfig}.
47
+ *
48
+ * @returns A {@link Retry} that is configured with the provided arguments.
49
+ */
50
+ function retry(fn, options) {
51
+ return new RetryImpl(fn, options);
52
+ }
53
+ exports.retry = retry;
54
+ /**
55
+ * The wraps a function into a retry promise.
56
+ *
57
+ * The promise resolves when the function resolves, or when the retry configuration determines so.
58
+ * The promise will reject is the inner function throws an error that is not retryable, or
59
+ * if the maximum amount of retries has been reached.
60
+ *
61
+ * @param fn - The inner function that will be retried.
62
+ * @param options - The retry configuration, such as defined in {@link RetryConfig}.
63
+ *
64
+ * @returns A promise wrapping the inner function with retry logic.
65
+ */
66
+ async function retryPromise(fn, options) {
67
+ const { retries, factor, minIntervalMs, maxIntervalMs, isRetryableError } = {
68
+ ...exports.DEFAULT_RETRY_CONFIG,
69
+ ...options,
70
+ };
71
+ return await promiseRetry({ retries, factor, minTimeout: minIntervalMs, maxTimeout: maxIntervalMs }, async (retryInner, attempt) => {
72
+ try {
73
+ return await fn(attempt);
74
+ }
75
+ catch (err) {
76
+ if (isRetryableError(err)) {
77
+ return retryInner(err);
78
+ }
79
+ throw err;
80
+ }
81
+ });
82
+ }
83
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;AAAA,8CAA+C;AAC/C,+CAA8E;AAE9E;;;;;;;;;GASG;AACU,QAAA,oBAAoB,GAAmC;IAClE,OAAO,EAAE,EAAE;IACX,MAAM,EAAE,CAAC;IACT,aAAa,EAAE,IAAI;IACnB,aAAa,EAAE,QAAQ;IACvB,gBAAgB,EAAE,GAAY,EAAE,CAAC,IAAI;CACtC,CAAC;AAsEF,MAAM,SACJ,SAAQ,uBAA+B;IAGpB,OAAO,CAAiB;IACxB,WAAW,CAA2B;IAEzD,YAAY,EAAoB,EAAE,OAAwB;QACxD,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,WAAW,GAAG,EAAE,GAAG,4BAAoB,EAAE,GAAG,OAAO,EAAE,CAAC;QAE3D,MAAM,OAAO,GAAG,CAAC,OAAe,EAAE,EAAE;YAClC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;gBACnB,OAAO;gBACP,WAAW,EAAE,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE;aACrC,CAAC,CAAC;YACH,OAAO,EAAE,EAAE,CAAC;QACd,CAAC,CAAC;QACF,IAAI,CAAC,OAAO,GAAG,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,CACF,WAGQ,EACR,UAGQ;QAER,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACpD,CAAC;CACF;AAED;;;;;;;GAOG;AACH,SAAgB,KAAK,CACnB,EAAoB,EACpB,OAAwB;IAExB,OAAO,IAAI,SAAS,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;AACpC,CAAC;AALD,sBAKC;AAED;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,YAAY,CACzB,EAAmC,EACnC,OAAwB;IAExB,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,GAAG;QAC1E,GAAG,4BAAoB;QACvB,GAAG,OAAO;KACX,CAAC;IAEF,OAAO,MAAM,YAAY,CACvB,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,EACzE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE;QAC5B,IAAI;YACF,OAAO,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC;SAC1B;QAAC,OAAO,GAAG,EAAE;YACZ,IAAI,gBAAgB,CAAC,GAAQ,CAAC,EAAE;gBAC9B,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC;aACxB;YACD,MAAM,GAAG,CAAC;SACX;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,84 @@
1
+ import { EmitterLike, Predicate } from "@infra-blocks/types";
2
+ /**
3
+ * Default retry configuration.
4
+ *
5
+ * The default configuration retries 60 times, with a factor of 1 and a minimum interval of 1000ms.
6
+ * The maximum interval is set to Infinity, meaning that there is no upper bound on the wait time.
7
+ *
8
+ * When no field is overridden, this config will result in retrying the function every second
9
+ * for a minute. Note that retries don't include the first attempt, so the inner function is
10
+ * called 61 times in total.
11
+ */
12
+ export declare const DEFAULT_RETRY_CONFIG: Required<RetryConfig<unknown>>;
13
+ /**
14
+ * Configuration for the retry function.
15
+ */
16
+ export interface RetryConfig<E = Error> {
17
+ /**
18
+ * The amount of retries that will be attempted. Note that the first attempt
19
+ * doest not count as a retry. In total, the amount of retries plus one
20
+ * attempts will be made, in the worst case scenario.
21
+ */
22
+ retries?: number;
23
+ /**
24
+ * The exponential backoff factor that will be used between retries.
25
+ * The actual wait time can be calculated as such:
26
+ * wait time = min((factor ^ retry) * minIntervalMs, maxIntervalMs)
27
+ */
28
+ factor?: number;
29
+ /**
30
+ * The minimum wait time, in milliseconds, before the first retry.
31
+ * When the factor is 1, the wait time between retries is constant and
32
+ * equal to this value.
33
+ */
34
+ minIntervalMs?: number;
35
+ /**
36
+ * The maximum wait time between retries can be bound using this option.
37
+ */
38
+ maxIntervalMs?: number;
39
+ /**
40
+ * A predicate used to determine if an error should warrant a retry or not.
41
+ *
42
+ * When the function returns true, a retry will be attempted. When it returns
43
+ * false, the process immediately fails and throws the error.
44
+ *
45
+ * @param err - The error received from the inner function.
46
+ * @returns True if the function should be retried, false otherwise.
47
+ */
48
+ isRetryableError?: Predicate<E>;
49
+ }
50
+ /**
51
+ * Events and their handler types for the {@link Retry}.
52
+ */
53
+ export type RetryEvents<E = Error> = {
54
+ /**
55
+ * This event is emitted at the beginning of every attempt.
56
+ *
57
+ * @param attempt - The attempt number. Starts with 1, and the first attempt does not count as a retry.
58
+ */
59
+ attempt: (params: {
60
+ attempt: number;
61
+ retryConfig: Required<RetryConfig<E>>;
62
+ }) => void;
63
+ };
64
+ /**
65
+ * Type returned by the {@link retry} function.
66
+ *
67
+ * This type is both a promise and an event emitter, where the events are described as in
68
+ * {@link RetryEvents}.
69
+ */
70
+ export interface Retry<T, E = Error> extends EmitterLike<RetryEvents<E>>, PromiseLike<T> {
71
+ }
72
+ /**
73
+ * The type of the inner function that is wrapped by the {@link retry} function.
74
+ */
75
+ export type RetryFunction<R> = () => Promise<R>;
76
+ /**
77
+ * Returns a {@link Retry} object that wraps the provided function.
78
+ *
79
+ * @param fn - The inner function that will be retried.
80
+ * @param options - The configuration for the retry process, such as defined in {@link RetryConfig}.
81
+ *
82
+ * @returns A {@link Retry} that is configured with the provided arguments.
83
+ */
84
+ export declare function retry<T, E = Error>(fn: RetryFunction<T>, options?: RetryConfig<E>): Retry<T, E>;
@@ -0,0 +1,81 @@
1
+ import { createRequire as _createRequire } from "module";
2
+ const __require = _createRequire(import.meta.url);
3
+ const promiseRetry = __require("promise-retry");
4
+ import { EmitterLikeBase } from "@infra-blocks/types";
5
+ /**
6
+ * Default retry configuration.
7
+ *
8
+ * The default configuration retries 60 times, with a factor of 1 and a minimum interval of 1000ms.
9
+ * The maximum interval is set to Infinity, meaning that there is no upper bound on the wait time.
10
+ *
11
+ * When no field is overridden, this config will result in retrying the function every second
12
+ * for a minute. Note that retries don't include the first attempt, so the inner function is
13
+ * called 61 times in total.
14
+ */
15
+ export const DEFAULT_RETRY_CONFIG = {
16
+ retries: 60,
17
+ factor: 1,
18
+ minIntervalMs: 1000,
19
+ maxIntervalMs: Infinity,
20
+ isRetryableError: () => true,
21
+ };
22
+ class RetryImpl extends EmitterLikeBase {
23
+ promise;
24
+ retryConfig;
25
+ constructor(fn, options) {
26
+ super();
27
+ this.retryConfig = { ...DEFAULT_RETRY_CONFIG, ...options };
28
+ const wrapper = (attempt) => {
29
+ this.emit("attempt", {
30
+ attempt,
31
+ retryConfig: { ...this.retryConfig },
32
+ });
33
+ return fn();
34
+ };
35
+ this.promise = retryPromise(wrapper, this.retryConfig);
36
+ }
37
+ then(onfulfilled, onrejected) {
38
+ return this.promise.then(onfulfilled, onrejected);
39
+ }
40
+ }
41
+ /**
42
+ * Returns a {@link Retry} object that wraps the provided function.
43
+ *
44
+ * @param fn - The inner function that will be retried.
45
+ * @param options - The configuration for the retry process, such as defined in {@link RetryConfig}.
46
+ *
47
+ * @returns A {@link Retry} that is configured with the provided arguments.
48
+ */
49
+ export function retry(fn, options) {
50
+ return new RetryImpl(fn, options);
51
+ }
52
+ /**
53
+ * The wraps a function into a retry promise.
54
+ *
55
+ * The promise resolves when the function resolves, or when the retry configuration determines so.
56
+ * The promise will reject is the inner function throws an error that is not retryable, or
57
+ * if the maximum amount of retries has been reached.
58
+ *
59
+ * @param fn - The inner function that will be retried.
60
+ * @param options - The retry configuration, such as defined in {@link RetryConfig}.
61
+ *
62
+ * @returns A promise wrapping the inner function with retry logic.
63
+ */
64
+ async function retryPromise(fn, options) {
65
+ const { retries, factor, minIntervalMs, maxIntervalMs, isRetryableError } = {
66
+ ...DEFAULT_RETRY_CONFIG,
67
+ ...options,
68
+ };
69
+ return await promiseRetry({ retries, factor, minTimeout: minIntervalMs, maxTimeout: maxIntervalMs }, async (retryInner, attempt) => {
70
+ try {
71
+ return await fn(attempt);
72
+ }
73
+ catch (err) {
74
+ if (isRetryableError(err)) {
75
+ return retryInner(err);
76
+ }
77
+ throw err;
78
+ }
79
+ });
80
+ }
81
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;AAAA,gDAA+C;AAC/C,OAAO,EAAe,eAAe,EAAa,MAAM,qBAAqB,CAAC;AAE9E;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAmC;IAClE,OAAO,EAAE,EAAE;IACX,MAAM,EAAE,CAAC;IACT,aAAa,EAAE,IAAI;IACnB,aAAa,EAAE,QAAQ;IACvB,gBAAgB,EAAE,GAAY,EAAE,CAAC,IAAI;CACtC,CAAC;AAsEF,MAAM,SACJ,SAAQ,eAA+B;IAGpB,OAAO,CAAiB;IACxB,WAAW,CAA2B;IAEzD,YAAY,EAAoB,EAAE,OAAwB;QACxD,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,WAAW,GAAG,EAAE,GAAG,oBAAoB,EAAE,GAAG,OAAO,EAAE,CAAC;QAE3D,MAAM,OAAO,GAAG,CAAC,OAAe,EAAE,EAAE;YAClC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;gBACnB,OAAO;gBACP,WAAW,EAAE,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE;aACrC,CAAC,CAAC;YACH,OAAO,EAAE,EAAE,CAAC;QACd,CAAC,CAAC;QACF,IAAI,CAAC,OAAO,GAAG,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,CACF,WAGQ,EACR,UAGQ;QAER,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACpD,CAAC;CACF;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,KAAK,CACnB,EAAoB,EACpB,OAAwB;IAExB,OAAO,IAAI,SAAS,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,YAAY,CACzB,EAAmC,EACnC,OAAwB;IAExB,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,GAAG;QAC1E,GAAG,oBAAoB;QACvB,GAAG,OAAO;KACX,CAAC;IAEF,OAAO,MAAM,YAAY,CACvB,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,EACzE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE;QAC5B,IAAI;YACF,OAAO,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC;SAC1B;QAAC,OAAO,GAAG,EAAE;YACZ,IAAI,gBAAgB,CAAC,GAAQ,CAAC,EAAE;gBAC9B,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC;aACxB;YACD,MAAM,GAAG,CAAC;SACX;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@infra-blocks/retry",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "This repository exports utilities for generic retry logic.",
5
+ "keywords": [
6
+ "retry",
7
+ "promise"
8
+ ],
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/infra-blocks/ts-retry.git"
12
+ },
13
+ "license": "ISC",
14
+ "author": "",
15
+ "type": "module",
16
+ "exports": {
17
+ "import": "./lib/esm/index.js",
18
+ "require": "./lib/cjs/index.js",
19
+ "default": "./lib/esm/index.js"
20
+ },
21
+ "files": [
22
+ "lib/**/*.{js,cjs,mjs,json,d.ts,map}"
23
+ ],
24
+ "scripts": {
25
+ "prebuild": "npm run clean",
26
+ "build": "tsc -b tsconfig.build.esm.json tsconfig.build.cjs.json",
27
+ "postbuild": "scripts/post-build.sh",
28
+ "clean": "rm -rf lib && rm -f infra-blocks-*.tgz",
29
+ "compile": "tsc",
30
+ "lint": "eslint --ext .js,.cjs,.mjs,.json,.ts --max-warnings 0 .",
31
+ "prepack": "npm run build",
32
+ "test": "npm run test:unit",
33
+ "test:coverage": "c8 npm run test",
34
+ "test:coverage:lcov": "c8 --reporter=lcov npm run test",
35
+ "test:integration": "mocha --config test/integration/.mocharc.js 'test/integration/**/*.spec.ts'",
36
+ "test:unit": "mocha --config test/unit/.mocharc.cjs 'test/unit/**/*.spec.ts'"
37
+ },
38
+ "dependencies": {
39
+ "@infra-blocks/types": "^0.6.0",
40
+ "promise-retry": "^2.0.1"
41
+ },
42
+ "devDependencies": {
43
+ "@infra-blocks/iter": "^0.2.7",
44
+ "@infra-blocks/test": "^0.4.0",
45
+ "@types/mocha": "^10.0.1",
46
+ "@types/node": "^20.10.3",
47
+ "@types/promise-retry": "^1.1.6",
48
+ "@types/verror": "^1.10.11",
49
+ "@typescript-eslint/eslint-plugin": "^5.59.8",
50
+ "@typescript-eslint/parser": "^5.59.8",
51
+ "c8": "^8.0.0",
52
+ "eslint": "^8.41.0",
53
+ "eslint-config-prettier": "^8.8.0",
54
+ "eslint-plugin-json-format": "^2.0.1",
55
+ "eslint-plugin-prettier": "^4.2.1",
56
+ "mocha": "^10.2.0",
57
+ "prettier": "^2.8.8",
58
+ "ts-node": "^10.9.1",
59
+ "typescript": "^5.0.4",
60
+ "verror": "^1.10.1"
61
+ },
62
+ "engines": {
63
+ "node": ">=18.0.0"
64
+ }
65
+ }