agent-yes 1.52.4 → 1.53.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/dist/{SUPPORTED_CLIS-CGHhOLoD.js → SUPPORTED_CLIS-CuEqXP9l.js} +14 -15
- package/dist/{agent-yes.config-B0st3al_.js → agent-yes.config-Dr2p5kBW.js} +6 -4
- package/dist/{agent-yes.config-BJbInLnS.js → agent-yes.config-mJP0MKqV.js} +1 -1
- package/dist/cli.js +3 -3
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/ts/{catcher.spec.ts → tryCatch.spec.ts} +99 -83
- package/ts/tryCatch.ts +23 -0
- package/ts/catcher.ts +0 -35
|
@@ -9,7 +9,7 @@ import { homedir } from "os";
|
|
|
9
9
|
import { fileURLToPath } from "url";
|
|
10
10
|
import { appendFileSync, createReadStream, createWriteStream, readFileSync as readFileSync$1, statSync as statSync$1, writeFileSync } from "node:fs";
|
|
11
11
|
import { fileURLToPath as fileURLToPath$1 } from "node:url";
|
|
12
|
-
import { ChildProcess, execFile, spawn as spawn$1, spawnSync } from "node:child_process";
|
|
12
|
+
import { ChildProcess, execFile, execSync as execSync$1, spawn as spawn$1, spawnSync } from "node:child_process";
|
|
13
13
|
import { StringDecoder } from "node:string_decoder";
|
|
14
14
|
import { aborted, callbackify, debuglog, inspect, promisify, stripVTControlCharacters } from "node:util";
|
|
15
15
|
import process$1, { execArgv, execPath, hrtime, platform as platform$1 } from "node:process";
|
|
@@ -9735,25 +9735,24 @@ async function saveDeprecatedLogFile(logFile, content, verbose) {
|
|
|
9735
9735
|
}
|
|
9736
9736
|
|
|
9737
9737
|
//#endregion
|
|
9738
|
-
//#region ts/
|
|
9738
|
+
//#region ts/tryCatch.ts
|
|
9739
9739
|
/**
|
|
9740
9740
|
* A utility function to wrap another function with a try-catch block.
|
|
9741
9741
|
* If an error occurs during the execution of the function, the provided
|
|
9742
9742
|
* catchFn is called with the error, the original function, and its arguments.
|
|
9743
9743
|
*
|
|
9744
|
-
* This function supports both direct invocation and curried usage.
|
|
9745
|
-
*
|
|
9746
9744
|
* @param catchFn - The function to call when an error occurs.
|
|
9747
|
-
* @param fn - The function to wrap
|
|
9745
|
+
* @param fn - The function to wrap.
|
|
9748
9746
|
* @returns A new function that wraps the original function with error handling.
|
|
9749
9747
|
*/
|
|
9750
|
-
function
|
|
9751
|
-
|
|
9752
|
-
return (...args)
|
|
9748
|
+
function tryCatch(catchFn, fn) {
|
|
9749
|
+
let attempts = 0;
|
|
9750
|
+
return function robustFn(...args) {
|
|
9753
9751
|
try {
|
|
9752
|
+
attempts++;
|
|
9754
9753
|
return fn(...args);
|
|
9755
9754
|
} catch (error) {
|
|
9756
|
-
return catchFn(error,
|
|
9755
|
+
return catchFn(error, attempts, robustFn, ...args);
|
|
9757
9756
|
}
|
|
9758
9757
|
};
|
|
9759
9758
|
}
|
|
@@ -9929,7 +9928,7 @@ function spawnAgent(options) {
|
|
|
9929
9928
|
logger.info(`[${cli}-yes] Spawned ${bin} with PID ${spawned.pid}`);
|
|
9930
9929
|
return spawned;
|
|
9931
9930
|
};
|
|
9932
|
-
return
|
|
9931
|
+
return tryCatch((error, attempts, spawn, ...args) => {
|
|
9933
9932
|
logger.error(`Fatal: Failed to start ${cli}.`);
|
|
9934
9933
|
const isNotFound = isCommandNotFoundError(error);
|
|
9935
9934
|
if (cliConf?.install && isNotFound) {
|
|
@@ -9940,10 +9939,10 @@ function spawnAgent(options) {
|
|
|
9940
9939
|
}
|
|
9941
9940
|
logger.info(`Please install the cli by run ${installCmd}`);
|
|
9942
9941
|
if (install) {
|
|
9943
|
-
logger.
|
|
9944
|
-
|
|
9942
|
+
logger.debug(`Attempting to install ${cli}...`);
|
|
9943
|
+
execSync$1(installCmd, { stdio: "inherit" });
|
|
9945
9944
|
logger.info(`${cli} installed successfully. Please rerun the command.`);
|
|
9946
|
-
return spawn();
|
|
9945
|
+
return spawn(...args);
|
|
9947
9946
|
} else {
|
|
9948
9947
|
logger.error(`If you did not installed it yet, Please install it first: ${installCmd}`);
|
|
9949
9948
|
throw error;
|
|
@@ -10118,7 +10117,7 @@ const globalAgentRegistry = new AgentRegistry();
|
|
|
10118
10117
|
|
|
10119
10118
|
//#endregion
|
|
10120
10119
|
//#region ts/index.ts
|
|
10121
|
-
const config = await import("./agent-yes.config-
|
|
10120
|
+
const config = await import("./agent-yes.config-mJP0MKqV.js").then((mod) => mod.default || mod);
|
|
10122
10121
|
const CLIS_CONFIG = config.clis;
|
|
10123
10122
|
/**
|
|
10124
10123
|
* Main function to run agent-cli with automatic yes/no responses
|
|
@@ -10735,4 +10734,4 @@ const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG);
|
|
|
10735
10734
|
|
|
10736
10735
|
//#endregion
|
|
10737
10736
|
export { AgentContext as a, config as i, CLIS_CONFIG as n, PidStore as o, agentYes as r, removeControlCharacters as s, SUPPORTED_CLIS as t };
|
|
10738
|
-
//# sourceMappingURL=SUPPORTED_CLIS-
|
|
10737
|
+
//# sourceMappingURL=SUPPORTED_CLIS-CuEqXP9l.js.map
|
|
@@ -239,9 +239,12 @@ function getDefaultConfig() {
|
|
|
239
239
|
/^.{0,4} ?1\. ?Yes/m,
|
|
240
240
|
/Press Enter to continue…/m
|
|
241
241
|
],
|
|
242
|
-
fatal: [
|
|
242
|
+
fatal: [
|
|
243
|
+
/⎿ Claude usage limit reached\./,
|
|
244
|
+
/^error: unknown option/,
|
|
245
|
+
/No conversation found to continue/
|
|
246
|
+
],
|
|
243
247
|
restoreArgs: ["--continue"],
|
|
244
|
-
restartWithoutContinueArg: [/No conversation found to continue/],
|
|
245
248
|
exitCommand: ["/exit"],
|
|
246
249
|
bunx: true,
|
|
247
250
|
defaultArgs: []
|
|
@@ -256,7 +259,6 @@ function getDefaultConfig() {
|
|
|
256
259
|
],
|
|
257
260
|
fatal: [/Error resuming session/, /No previous sessions found for this project./],
|
|
258
261
|
restoreArgs: ["--resume"],
|
|
259
|
-
restartWithoutContinueArg: [/No previous sessions found for this project\./, /Error resuming session/],
|
|
260
262
|
exitCommand: ["/chat save ${PWD}", "/quit"]
|
|
261
263
|
},
|
|
262
264
|
codex: {
|
|
@@ -330,4 +332,4 @@ function getDefaultConfig() {
|
|
|
330
332
|
|
|
331
333
|
//#endregion
|
|
332
334
|
export { agent_yes_config_default as t };
|
|
333
|
-
//# sourceMappingURL=agent-yes.config-
|
|
335
|
+
//# sourceMappingURL=agent-yes.config-Dr2p5kBW.js.map
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import { c as __toESM, i as __commonJSMin, r as require_ms, t as logger } from "./logger-DH1Rx9HI.js";
|
|
3
|
-
import "./agent-yes.config-
|
|
4
|
-
import { o as PidStore, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-
|
|
3
|
+
import "./agent-yes.config-Dr2p5kBW.js";
|
|
4
|
+
import { o as PidStore, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CuEqXP9l.js";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { argv } from "process";
|
|
7
7
|
import { spawn } from "child_process";
|
|
@@ -4505,7 +4505,7 @@ const Yargs = YargsFactory(esm_default);
|
|
|
4505
4505
|
//#endregion
|
|
4506
4506
|
//#region package.json
|
|
4507
4507
|
var name = "agent-yes";
|
|
4508
|
-
var version = "1.
|
|
4508
|
+
var version = "1.53.0";
|
|
4509
4509
|
|
|
4510
4510
|
//#endregion
|
|
4511
4511
|
//#region ts/parseCliArgs.ts
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import "./logger-DH1Rx9HI.js";
|
|
2
|
-
import { a as AgentContext, i as config, n as CLIS_CONFIG, r as agentYes, s as removeControlCharacters } from "./SUPPORTED_CLIS-
|
|
2
|
+
import { a as AgentContext, i as config, n as CLIS_CONFIG, r as agentYes, s as removeControlCharacters } from "./SUPPORTED_CLIS-CuEqXP9l.js";
|
|
3
3
|
|
|
4
4
|
export { AgentContext, CLIS_CONFIG, config, agentYes as default, removeControlCharacters };
|
package/package.json
CHANGED
|
@@ -1,70 +1,17 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
describe("catcher", () => {
|
|
5
|
-
describe("curried overload", () => {
|
|
6
|
-
it("should return a function when called with only catchFn", () => {
|
|
7
|
-
const catchFn = () => "error";
|
|
8
|
-
const result = catcher(catchFn);
|
|
9
|
-
expect(typeof result).toBe("function");
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it("should catch errors and call catchFn with error, function, and args", () => {
|
|
13
|
-
let catchedError: unknown;
|
|
14
|
-
let catchedFn: unknown;
|
|
15
|
-
let catchedArgs: unknown[];
|
|
16
|
-
const catchFn = (error: unknown, fn: unknown, ...args: unknown[]) => {
|
|
17
|
-
catchedError = error;
|
|
18
|
-
catchedFn = fn;
|
|
19
|
-
catchedArgs = args;
|
|
20
|
-
return "caught";
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
let calledArgs: unknown[] = [];
|
|
24
|
-
const errorFn = (...args: unknown[]) => {
|
|
25
|
-
calledArgs = args;
|
|
26
|
-
throw new Error("test error");
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const wrappedFn = catcher(catchFn)(errorFn);
|
|
30
|
-
const result = wrappedFn("arg1", "arg2");
|
|
31
|
-
|
|
32
|
-
expect(result).toBe("caught");
|
|
33
|
-
expect(catchedError).toBeInstanceOf(Error);
|
|
34
|
-
expect(catchedFn).toBe(errorFn);
|
|
35
|
-
expect(catchedArgs).toEqual(["arg1", "arg2"]);
|
|
36
|
-
expect(calledArgs).toEqual(["arg1", "arg2"]);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("should return normal result when no error occurs", () => {
|
|
40
|
-
let catchCalled = false;
|
|
41
|
-
const catchFn = () => {
|
|
42
|
-
catchCalled = true;
|
|
43
|
-
return "error";
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
let calledArgs: unknown[] = [];
|
|
47
|
-
const normalFn = (...args: unknown[]) => {
|
|
48
|
-
calledArgs = args;
|
|
49
|
-
return "success";
|
|
50
|
-
};
|
|
2
|
+
import { tryCatch } from "./tryCatch";
|
|
51
3
|
|
|
52
|
-
|
|
53
|
-
const result = wrappedFn("arg1", "arg2");
|
|
54
|
-
|
|
55
|
-
expect(result).toBe("success");
|
|
56
|
-
expect(catchCalled).toBe(false);
|
|
57
|
-
expect(calledArgs).toEqual(["arg1", "arg2"]);
|
|
58
|
-
});
|
|
59
|
-
});
|
|
4
|
+
describe("tryCatch", () => {
|
|
60
5
|
|
|
61
6
|
describe("direct overload", () => {
|
|
62
|
-
it("should catch errors and call catchFn with error,
|
|
7
|
+
it("should catch errors and call catchFn with error, attempts, robustFn, and args", () => {
|
|
63
8
|
let catchedError: unknown;
|
|
9
|
+
let catchedAttempts: unknown;
|
|
64
10
|
let catchedFn: unknown;
|
|
65
11
|
let catchedArgs: unknown[];
|
|
66
|
-
const catchFn = (error: unknown, fn: unknown, ...args: unknown[]) => {
|
|
12
|
+
const catchFn = (error: unknown, attempts: number, fn: unknown, ...args: unknown[]) => {
|
|
67
13
|
catchedError = error;
|
|
14
|
+
catchedAttempts = attempts;
|
|
68
15
|
catchedFn = fn;
|
|
69
16
|
catchedArgs = args;
|
|
70
17
|
return "caught";
|
|
@@ -76,12 +23,13 @@ describe("catcher", () => {
|
|
|
76
23
|
throw new Error("test error");
|
|
77
24
|
};
|
|
78
25
|
|
|
79
|
-
const wrappedFn =
|
|
26
|
+
const wrappedFn = tryCatch(catchFn, errorFn);
|
|
80
27
|
const result = wrappedFn("arg1", "arg2");
|
|
81
28
|
|
|
82
29
|
expect(result).toBe("caught");
|
|
83
30
|
expect(catchedError).toBeInstanceOf(Error);
|
|
84
|
-
expect(
|
|
31
|
+
expect(catchedAttempts).toBe(1);
|
|
32
|
+
expect(catchedFn).toBe(wrappedFn);
|
|
85
33
|
expect(catchedArgs).toEqual(["arg1", "arg2"]);
|
|
86
34
|
expect(calledArgs).toEqual(["arg1", "arg2"]);
|
|
87
35
|
});
|
|
@@ -99,7 +47,7 @@ describe("catcher", () => {
|
|
|
99
47
|
return "success";
|
|
100
48
|
};
|
|
101
49
|
|
|
102
|
-
const wrappedFn =
|
|
50
|
+
const wrappedFn = tryCatch(catchFn, normalFn);
|
|
103
51
|
const result = wrappedFn("arg1", "arg2");
|
|
104
52
|
|
|
105
53
|
expect(result).toBe("success");
|
|
@@ -112,7 +60,7 @@ describe("catcher", () => {
|
|
|
112
60
|
it("should handle different error types and pass function context", () => {
|
|
113
61
|
const results: unknown[] = [];
|
|
114
62
|
const functions: unknown[] = [];
|
|
115
|
-
const catchFn = (error: unknown, fn: unknown, ..._args: unknown[]) => {
|
|
63
|
+
const catchFn = (error: unknown, _attempts: number, fn: unknown, ..._args: unknown[]) => {
|
|
116
64
|
results.push(error);
|
|
117
65
|
functions.push(fn);
|
|
118
66
|
return "handled";
|
|
@@ -122,37 +70,39 @@ describe("catcher", () => {
|
|
|
122
70
|
const stringErrorFn = () => {
|
|
123
71
|
throw "string error";
|
|
124
72
|
};
|
|
125
|
-
const wrappedStringFn =
|
|
73
|
+
const wrappedStringFn = tryCatch(catchFn, stringErrorFn);
|
|
126
74
|
expect(wrappedStringFn()).toBe("handled");
|
|
127
75
|
expect(results[0]).toBe("string error");
|
|
128
|
-
expect(functions[0]).toBe(
|
|
76
|
+
expect(functions[0]).toBe(wrappedStringFn);
|
|
129
77
|
|
|
130
78
|
// Object error
|
|
131
79
|
const objectError = { message: "object error" };
|
|
132
80
|
const objectErrorFn = () => {
|
|
133
81
|
throw objectError;
|
|
134
82
|
};
|
|
135
|
-
const wrappedObjectFn =
|
|
83
|
+
const wrappedObjectFn = tryCatch(catchFn, objectErrorFn);
|
|
136
84
|
expect(wrappedObjectFn()).toBe("handled");
|
|
137
85
|
expect(results[1]).toBe(objectError);
|
|
138
|
-
expect(functions[1]).toBe(
|
|
86
|
+
expect(functions[1]).toBe(wrappedObjectFn);
|
|
139
87
|
|
|
140
88
|
// null error
|
|
141
89
|
const nullErrorFn = () => {
|
|
142
90
|
throw null;
|
|
143
91
|
};
|
|
144
|
-
const wrappedNullFn =
|
|
92
|
+
const wrappedNullFn = tryCatch(catchFn, nullErrorFn);
|
|
145
93
|
expect(wrappedNullFn()).toBe("handled");
|
|
146
94
|
expect(results[2]).toBe(null);
|
|
147
|
-
expect(functions[2]).toBe(
|
|
95
|
+
expect(functions[2]).toBe(wrappedNullFn);
|
|
148
96
|
});
|
|
149
97
|
|
|
150
98
|
it("should preserve function parameters and pass them to catchFn", () => {
|
|
151
99
|
let caughtError: unknown;
|
|
100
|
+
let caughtAttempts: unknown;
|
|
152
101
|
let caughtFn: unknown;
|
|
153
102
|
let caughtArgs: unknown[];
|
|
154
|
-
const catchFn = (error: unknown, fn: unknown, ...args: unknown[]) => {
|
|
103
|
+
const catchFn = (error: unknown, attempts: number, fn: unknown, ...args: unknown[]) => {
|
|
155
104
|
caughtError = error;
|
|
105
|
+
caughtAttempts = attempts;
|
|
156
106
|
caughtFn = fn;
|
|
157
107
|
caughtArgs = args;
|
|
158
108
|
return "caught";
|
|
@@ -165,7 +115,7 @@ describe("catcher", () => {
|
|
|
165
115
|
return `${a}-${b}-${c}`;
|
|
166
116
|
};
|
|
167
117
|
|
|
168
|
-
const wrappedFn =
|
|
118
|
+
const wrappedFn = tryCatch(catchFn, testFn);
|
|
169
119
|
|
|
170
120
|
// Normal execution
|
|
171
121
|
expect(wrappedFn(3, "test", true)).toBe("3-test-true");
|
|
@@ -175,16 +125,19 @@ describe("catcher", () => {
|
|
|
175
125
|
expect(wrappedFn(10, "error", false)).toBe("caught");
|
|
176
126
|
expect(testArgs).toEqual([10, "error", false]);
|
|
177
127
|
expect(caughtError).toBeInstanceOf(Error);
|
|
178
|
-
expect(
|
|
128
|
+
expect(caughtAttempts).toBe(2);
|
|
129
|
+
expect(caughtFn).toBe(wrappedFn);
|
|
179
130
|
expect(caughtArgs).toEqual([10, "error", false]);
|
|
180
131
|
});
|
|
181
132
|
|
|
182
133
|
it("should handle functions with no parameters", () => {
|
|
183
134
|
let caughtError: unknown;
|
|
135
|
+
let caughtAttempts: unknown;
|
|
184
136
|
let caughtFn: unknown;
|
|
185
137
|
let caughtArgs: unknown[];
|
|
186
|
-
const catchFn = (error: unknown, fn: unknown, ...args: unknown[]) => {
|
|
138
|
+
const catchFn = (error: unknown, attempts: number, fn: unknown, ...args: unknown[]) => {
|
|
187
139
|
caughtError = error;
|
|
140
|
+
caughtAttempts = attempts;
|
|
188
141
|
caughtFn = fn;
|
|
189
142
|
caughtArgs = args;
|
|
190
143
|
return "no params caught";
|
|
@@ -196,13 +149,14 @@ describe("catcher", () => {
|
|
|
196
149
|
throw new Error("no params error");
|
|
197
150
|
};
|
|
198
151
|
|
|
199
|
-
const wrappedFn =
|
|
152
|
+
const wrappedFn = tryCatch(catchFn, noParamsFn);
|
|
200
153
|
const result = wrappedFn();
|
|
201
154
|
|
|
202
155
|
expect(result).toBe("no params caught");
|
|
203
156
|
expect(called).toBe(true);
|
|
204
157
|
expect(caughtError).toBeInstanceOf(Error);
|
|
205
|
-
expect(
|
|
158
|
+
expect(caughtAttempts).toBe(1);
|
|
159
|
+
expect(caughtFn).toBe(wrappedFn);
|
|
206
160
|
expect(caughtArgs).toEqual([]);
|
|
207
161
|
});
|
|
208
162
|
|
|
@@ -210,26 +164,88 @@ describe("catcher", () => {
|
|
|
210
164
|
const catchFn = () => null;
|
|
211
165
|
|
|
212
166
|
// Function returning number
|
|
213
|
-
const numberFn =
|
|
167
|
+
const numberFn = tryCatch(catchFn, () => 42);
|
|
214
168
|
expect(numberFn()).toBe(42);
|
|
215
169
|
|
|
216
170
|
// Function returning object
|
|
217
171
|
const obj = { key: "value" };
|
|
218
|
-
const objectFn =
|
|
172
|
+
const objectFn = tryCatch(catchFn, () => obj);
|
|
219
173
|
expect(objectFn()).toBe(obj);
|
|
220
174
|
|
|
221
175
|
// Function returning undefined
|
|
222
|
-
const undefinedFn =
|
|
176
|
+
const undefinedFn = tryCatch(catchFn, () => undefined);
|
|
223
177
|
expect(undefinedFn()).toBeUndefined();
|
|
224
178
|
});
|
|
225
179
|
});
|
|
226
180
|
|
|
181
|
+
describe("attempts tracking", () => {
|
|
182
|
+
it("should increment attempts on each call", () => {
|
|
183
|
+
const attemptsList: number[] = [];
|
|
184
|
+
const catchFn = (_error: unknown, attempts: number) => {
|
|
185
|
+
attemptsList.push(attempts);
|
|
186
|
+
return "caught";
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const errorFn = () => {
|
|
190
|
+
throw new Error("fail");
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const wrappedFn = tryCatch(catchFn, errorFn);
|
|
194
|
+
wrappedFn();
|
|
195
|
+
wrappedFn();
|
|
196
|
+
wrappedFn();
|
|
197
|
+
|
|
198
|
+
expect(attemptsList).toEqual([1, 2, 3]);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("should count attempts for both successful and failed calls", () => {
|
|
202
|
+
let lastAttempts = 0;
|
|
203
|
+
const catchFn = (_error: unknown, attempts: number) => {
|
|
204
|
+
lastAttempts = attempts;
|
|
205
|
+
return -1;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
let callCount = 0;
|
|
209
|
+
const sometimesFails = () => {
|
|
210
|
+
callCount++;
|
|
211
|
+
if (callCount % 2 === 0) throw new Error("even call");
|
|
212
|
+
return callCount;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const wrappedFn = tryCatch(catchFn, sometimesFails);
|
|
216
|
+
expect(wrappedFn()).toBe(1); // attempt 1, success
|
|
217
|
+
expect(wrappedFn()).toBe(-1); // attempt 2, fail
|
|
218
|
+
expect(lastAttempts).toBe(2);
|
|
219
|
+
expect(wrappedFn()).toBe(3); // attempt 3, success
|
|
220
|
+
expect(wrappedFn()).toBe(-1); // attempt 4, fail
|
|
221
|
+
expect(lastAttempts).toBe(4);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("should allow retry via robustFn", () => {
|
|
225
|
+
let callCount = 0;
|
|
226
|
+
const catchFn = (_error: unknown, attempts: number, retry: () => number) => {
|
|
227
|
+
if (attempts < 3) return retry();
|
|
228
|
+
return -1;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const unreliableFn = () => {
|
|
232
|
+
callCount++;
|
|
233
|
+
if (callCount < 3) throw new Error("not yet");
|
|
234
|
+
return 42;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const wrappedFn = tryCatch(catchFn, unreliableFn);
|
|
238
|
+
expect(wrappedFn()).toBe(42);
|
|
239
|
+
expect(callCount).toBe(3);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
227
243
|
describe("type safety", () => {
|
|
228
244
|
it("should maintain function signature", () => {
|
|
229
|
-
const catchFn = (_error: unknown, _fn: unknown, ..._args: unknown[]) => "error";
|
|
245
|
+
const catchFn = (_error: unknown, _attempts: number, _fn: unknown, ..._args: unknown[]) => "error";
|
|
230
246
|
const originalFn = (a: number, b: string): string => `${a}-${b}`;
|
|
231
247
|
|
|
232
|
-
const wrappedFn =
|
|
248
|
+
const wrappedFn = tryCatch(catchFn, originalFn);
|
|
233
249
|
|
|
234
250
|
// This should be type-safe
|
|
235
251
|
const result: string = wrappedFn(1, "test");
|
|
@@ -239,7 +255,7 @@ describe("catcher", () => {
|
|
|
239
255
|
it("should pass function reference and arguments to catchFn", () => {
|
|
240
256
|
let capturedFn: unknown;
|
|
241
257
|
let capturedArgs: unknown[];
|
|
242
|
-
const catchFn = (
|
|
258
|
+
const catchFn = (_error: unknown, _attempts: number, fn: unknown, ...args: unknown[]) => {
|
|
243
259
|
capturedFn = fn;
|
|
244
260
|
capturedArgs = args;
|
|
245
261
|
return "handled";
|
|
@@ -249,10 +265,10 @@ describe("catcher", () => {
|
|
|
249
265
|
throw new Error("test");
|
|
250
266
|
};
|
|
251
267
|
|
|
252
|
-
const wrappedFn =
|
|
268
|
+
const wrappedFn = tryCatch(catchFn, testFn);
|
|
253
269
|
wrappedFn(42, "hello");
|
|
254
270
|
|
|
255
|
-
expect(capturedFn).toBe(
|
|
271
|
+
expect(capturedFn).toBe(wrappedFn);
|
|
256
272
|
expect(capturedArgs).toEqual([42, "hello"]);
|
|
257
273
|
});
|
|
258
274
|
});
|
package/ts/tryCatch.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A utility function to wrap another function with a try-catch block.
|
|
3
|
+
* If an error occurs during the execution of the function, the provided
|
|
4
|
+
* catchFn is called with the error, the original function, and its arguments.
|
|
5
|
+
*
|
|
6
|
+
* @param catchFn - The function to call when an error occurs.
|
|
7
|
+
* @param fn - The function to wrap.
|
|
8
|
+
* @returns A new function that wraps the original function with error handling.
|
|
9
|
+
*/
|
|
10
|
+
export function tryCatch<F extends (...args: any[]) => any, R>(
|
|
11
|
+
catchFn: (error: unknown, attempts: number, robustFn: F, ...args: Parameters<F>) => R,
|
|
12
|
+
fn: F,
|
|
13
|
+
) {
|
|
14
|
+
let attempts = 0;
|
|
15
|
+
return function robustFn(...args: Parameters<F>) {
|
|
16
|
+
try {
|
|
17
|
+
attempts++;
|
|
18
|
+
return fn(...args);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
return catchFn(error, attempts, robustFn as F, ...args);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
package/ts/catcher.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// curried overload
|
|
2
|
-
export function catcher<F extends (...args: any[]) => any, R>(
|
|
3
|
-
catchFn: (error: unknown, fn: F, ...args: Parameters<F>) => R,
|
|
4
|
-
): (fn: F) => (...args: Parameters<F>) => ReturnType<F> | R;
|
|
5
|
-
|
|
6
|
-
// direct overload
|
|
7
|
-
export function catcher<F extends (...args: any[]) => any, R>(
|
|
8
|
-
catchFn: (error: unknown, fn: F, ...args: Parameters<F>) => R,
|
|
9
|
-
fn: F,
|
|
10
|
-
): (...args: Parameters<F>) => ReturnType<F> | R;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* A utility function to wrap another function with a try-catch block.
|
|
14
|
-
* If an error occurs during the execution of the function, the provided
|
|
15
|
-
* catchFn is called with the error, the original function, and its arguments.
|
|
16
|
-
*
|
|
17
|
-
* This function supports both direct invocation and curried usage.
|
|
18
|
-
*
|
|
19
|
-
* @param catchFn - The function to call when an error occurs.
|
|
20
|
-
* @param fn - The function to wrap (optional for curried usage).
|
|
21
|
-
* @returns A new function that wraps the original function with error handling.
|
|
22
|
-
*/
|
|
23
|
-
export function catcher<F extends (...args: any[]) => any, R>(
|
|
24
|
-
catchFn: (error: unknown, fn: F, ...args: Parameters<F>) => R,
|
|
25
|
-
fn?: F,
|
|
26
|
-
) {
|
|
27
|
-
if (!fn) return (fn: F) => catcher(catchFn, fn) as any;
|
|
28
|
-
return (...args: Parameters<F>) => {
|
|
29
|
-
try {
|
|
30
|
-
return fn(...args);
|
|
31
|
-
} catch (error) {
|
|
32
|
-
return catchFn(error, fn, ...args);
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
}
|