agent-yes 1.52.4 → 1.53.1

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.
@@ -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";
@@ -9370,7 +9370,7 @@ var SqliteAdapter = class {
9370
9370
  if (typeof this.db.query === "function") return this.db.query(sql).all(params);
9371
9371
  else return this.db.prepare(sql).all(...params);
9372
9372
  } catch (error) {
9373
- logger.warn("[SqliteAdapter] Query failed:", error);
9373
+ logger.debug("[SqliteAdapter] Query failed:", error);
9374
9374
  return [];
9375
9375
  }
9376
9376
  }
@@ -9381,7 +9381,7 @@ var SqliteAdapter = class {
9381
9381
  return {};
9382
9382
  } else return this.db.prepare(sql).run(...params);
9383
9383
  } catch (error) {
9384
- logger.warn("[SqliteAdapter] Run failed:", error);
9384
+ logger.debug("[SqliteAdapter] Run failed:", error);
9385
9385
  return {};
9386
9386
  }
9387
9387
  }
@@ -9389,7 +9389,7 @@ var SqliteAdapter = class {
9389
9389
  try {
9390
9390
  if (this.db?.close) this.db.close();
9391
9391
  } catch (error) {
9392
- logger.warn("[SqliteAdapter] Close failed:", error);
9392
+ logger.debug("[SqliteAdapter] Close failed:", error);
9393
9393
  }
9394
9394
  }
9395
9395
  isReady() {
@@ -9735,25 +9735,24 @@ async function saveDeprecatedLogFile(logFile, content, verbose) {
9735
9735
  }
9736
9736
 
9737
9737
  //#endregion
9738
- //#region ts/catcher.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 (optional for curried usage).
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 catcher(catchFn, fn) {
9751
- if (!fn) return (fn) => catcher(catchFn, fn);
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, fn, ...args);
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 catcher((error, _fn, ..._args) => {
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.info(`Attempting to install ${cli}...`);
9944
- execaCommandSync(installCmd, { stdio: "inherit" });
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-BJbInLnS.js").then((mod) => mod.default || mod);
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-CGHhOLoD.js.map
10737
+ //# sourceMappingURL=SUPPORTED_CLIS-I-XMBksk.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: [/⎿ Claude usage limit reached\./, /^error: unknown option/],
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-B0st3al_.js.map
335
+ //# sourceMappingURL=agent-yes.config-Dr2p5kBW.js.map
@@ -1,4 +1,4 @@
1
1
  import "./logger-DH1Rx9HI.js";
2
- import { t as agent_yes_config_default } from "./agent-yes.config-B0st3al_.js";
2
+ import { t as agent_yes_config_default } from "./agent-yes.config-Dr2p5kBW.js";
3
3
 
4
4
  export { agent_yes_config_default as default };
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-B0st3al_.js";
4
- import { o as PidStore, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-CGHhOLoD.js";
3
+ import "./agent-yes.config-Dr2p5kBW.js";
4
+ import { o as PidStore, t as SUPPORTED_CLIS } from "./SUPPORTED_CLIS-I-XMBksk.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.52.4";
4508
+ var version = "1.53.1";
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-CGHhOLoD.js";
2
+ import { a as AgentContext, i as config, n as CLIS_CONFIG, r as agentYes, s as removeControlCharacters } from "./SUPPORTED_CLIS-I-XMBksk.js";
3
3
 
4
4
  export { AgentContext, CLIS_CONFIG, config, agentYes as default, removeControlCharacters };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.52.4",
3
+ "version": "1.53.1",
4
4
  "description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
5
5
  "keywords": [
6
6
  "ai",
@@ -72,7 +72,7 @@ export class SqliteAdapter {
72
72
  return this.db.prepare(sql).all(...params);
73
73
  }
74
74
  } catch (error) {
75
- logger.warn("[SqliteAdapter] Query failed:", error);
75
+ logger.debug("[SqliteAdapter] Query failed:", error);
76
76
  return [];
77
77
  }
78
78
  }
@@ -88,7 +88,7 @@ export class SqliteAdapter {
88
88
  return this.db.prepare(sql).run(...params);
89
89
  }
90
90
  } catch (error) {
91
- logger.warn("[SqliteAdapter] Run failed:", error);
91
+ logger.debug("[SqliteAdapter] Run failed:", error);
92
92
  return {};
93
93
  }
94
94
  }
@@ -99,7 +99,7 @@ export class SqliteAdapter {
99
99
  this.db.close();
100
100
  }
101
101
  } catch (error) {
102
- logger.warn("[SqliteAdapter] Close failed:", error);
102
+ logger.debug("[SqliteAdapter] Close failed:", error);
103
103
  }
104
104
  }
105
105
 
@@ -1,70 +1,17 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { catcher } from "./catcher";
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
- const wrappedFn = catcher(catchFn)(normalFn);
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, function, and args directly", () => {
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 = catcher(catchFn, errorFn);
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(catchedFn).toBe(errorFn);
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 = catcher(catchFn, normalFn);
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 = catcher(catchFn, stringErrorFn);
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(stringErrorFn);
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 = catcher(catchFn, objectErrorFn);
83
+ const wrappedObjectFn = tryCatch(catchFn, objectErrorFn);
136
84
  expect(wrappedObjectFn()).toBe("handled");
137
85
  expect(results[1]).toBe(objectError);
138
- expect(functions[1]).toBe(objectErrorFn);
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 = catcher(catchFn, nullErrorFn);
92
+ const wrappedNullFn = tryCatch(catchFn, nullErrorFn);
145
93
  expect(wrappedNullFn()).toBe("handled");
146
94
  expect(results[2]).toBe(null);
147
- expect(functions[2]).toBe(nullErrorFn);
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 = catcher(catchFn, testFn);
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(caughtFn).toBe(testFn);
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 = catcher(catchFn, noParamsFn);
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(caughtFn).toBe(noParamsFn);
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 = catcher(catchFn, () => 42);
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 = catcher(catchFn, () => obj);
172
+ const objectFn = tryCatch(catchFn, () => obj);
219
173
  expect(objectFn()).toBe(obj);
220
174
 
221
175
  // Function returning undefined
222
- const undefinedFn = catcher(catchFn, () => undefined);
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 = catcher(catchFn, originalFn);
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 = (error: unknown, fn: unknown, ...args: unknown[]) => {
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 = catcher(catchFn, testFn);
268
+ const wrappedFn = tryCatch(catchFn, testFn);
253
269
  wrappedFn(42, "hello");
254
270
 
255
- expect(capturedFn).toBe(testFn);
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
- }