@dizzlkheinz/ynab-mcpb 0.18.4 → 0.19.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.
Files changed (343) hide show
  1. package/CLAUDE.md +87 -8
  2. package/bin/ynab-mcp-server.cjs +2 -2
  3. package/bin/ynab-mcp-server.js +3 -3
  4. package/biome.json +39 -0
  5. package/dist/bundle/index.cjs +67 -67
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.js +27 -27
  8. package/dist/server/YNABMCPServer.d.ts +3 -4
  9. package/dist/server/YNABMCPServer.js +111 -116
  10. package/dist/server/budgetResolver.d.ts +6 -5
  11. package/dist/server/budgetResolver.js +46 -36
  12. package/dist/server/cacheKeys.js +6 -6
  13. package/dist/server/cacheManager.js +14 -11
  14. package/dist/server/completions.d.ts +2 -2
  15. package/dist/server/completions.js +20 -15
  16. package/dist/server/config.d.ts +10 -5
  17. package/dist/server/config.js +24 -7
  18. package/dist/server/deltaCache.d.ts +2 -2
  19. package/dist/server/deltaCache.js +22 -16
  20. package/dist/server/deltaCache.merge.d.ts +2 -2
  21. package/dist/server/diagnostics.d.ts +4 -4
  22. package/dist/server/diagnostics.js +38 -32
  23. package/dist/server/errorHandler.d.ts +5 -12
  24. package/dist/server/errorHandler.js +219 -217
  25. package/dist/server/prompts.d.ts +2 -2
  26. package/dist/server/prompts.js +45 -45
  27. package/dist/server/rateLimiter.js +4 -4
  28. package/dist/server/requestLogger.d.ts +1 -1
  29. package/dist/server/requestLogger.js +40 -35
  30. package/dist/server/resources.d.ts +3 -3
  31. package/dist/server/resources.js +55 -52
  32. package/dist/server/responseFormatter.js +6 -6
  33. package/dist/server/securityMiddleware.d.ts +2 -2
  34. package/dist/server/securityMiddleware.js +22 -20
  35. package/dist/server/serverKnowledgeStore.js +1 -1
  36. package/dist/server/toolRegistry.d.ts +3 -3
  37. package/dist/server/toolRegistry.js +47 -40
  38. package/dist/tools/__tests__/deltaTestUtils.d.ts +3 -3
  39. package/dist/tools/__tests__/deltaTestUtils.js +2 -2
  40. package/dist/tools/accountTools.d.ts +9 -8
  41. package/dist/tools/accountTools.js +47 -47
  42. package/dist/tools/adapters.d.ts +13 -8
  43. package/dist/tools/adapters.js +21 -11
  44. package/dist/tools/budgetTools.d.ts +8 -7
  45. package/dist/tools/budgetTools.js +22 -22
  46. package/dist/tools/categoryTools.d.ts +9 -8
  47. package/dist/tools/categoryTools.js +68 -59
  48. package/dist/tools/compareTransactions/formatter.d.ts +3 -3
  49. package/dist/tools/compareTransactions/formatter.js +9 -9
  50. package/dist/tools/compareTransactions/index.d.ts +6 -6
  51. package/dist/tools/compareTransactions/index.js +58 -43
  52. package/dist/tools/compareTransactions/matcher.d.ts +1 -1
  53. package/dist/tools/compareTransactions/matcher.js +28 -15
  54. package/dist/tools/compareTransactions/parser.d.ts +2 -2
  55. package/dist/tools/compareTransactions/parser.js +144 -138
  56. package/dist/tools/compareTransactions/types.d.ts +4 -4
  57. package/dist/tools/compareTransactions.d.ts +1 -1
  58. package/dist/tools/compareTransactions.js +1 -1
  59. package/dist/tools/deltaFetcher.d.ts +2 -2
  60. package/dist/tools/deltaFetcher.js +16 -15
  61. package/dist/tools/deltaSupport.d.ts +4 -4
  62. package/dist/tools/deltaSupport.js +35 -41
  63. package/dist/tools/exportTransactions.d.ts +5 -4
  64. package/dist/tools/exportTransactions.js +61 -59
  65. package/dist/tools/monthTools.d.ts +7 -6
  66. package/dist/tools/monthTools.js +31 -29
  67. package/dist/tools/payeeTools.d.ts +7 -6
  68. package/dist/tools/payeeTools.js +28 -28
  69. package/dist/tools/reconcileAdapter.d.ts +2 -2
  70. package/dist/tools/reconcileAdapter.js +19 -12
  71. package/dist/tools/reconciliation/analyzer.d.ts +4 -4
  72. package/dist/tools/reconciliation/analyzer.js +73 -59
  73. package/dist/tools/reconciliation/csvParser.d.ts +3 -3
  74. package/dist/tools/reconciliation/csvParser.js +128 -104
  75. package/dist/tools/reconciliation/executor.d.ts +4 -4
  76. package/dist/tools/reconciliation/executor.js +148 -109
  77. package/dist/tools/reconciliation/index.d.ts +10 -10
  78. package/dist/tools/reconciliation/index.js +96 -83
  79. package/dist/tools/reconciliation/matcher.d.ts +3 -3
  80. package/dist/tools/reconciliation/matcher.js +17 -16
  81. package/dist/tools/reconciliation/payeeNormalizer.js +19 -8
  82. package/dist/tools/reconciliation/recommendationEngine.d.ts +1 -1
  83. package/dist/tools/reconciliation/recommendationEngine.js +40 -40
  84. package/dist/tools/reconciliation/reportFormatter.d.ts +2 -2
  85. package/dist/tools/reconciliation/reportFormatter.js +59 -58
  86. package/dist/tools/reconciliation/signDetector.d.ts +1 -1
  87. package/dist/tools/reconciliation/types.d.ts +16 -16
  88. package/dist/tools/reconciliation/ynabAdapter.d.ts +2 -2
  89. package/dist/tools/schemas/common.d.ts +1 -1
  90. package/dist/tools/schemas/common.js +1 -1
  91. package/dist/tools/schemas/outputs/accountOutputs.d.ts +1 -1
  92. package/dist/tools/schemas/outputs/accountOutputs.js +24 -18
  93. package/dist/tools/schemas/outputs/budgetOutputs.d.ts +1 -1
  94. package/dist/tools/schemas/outputs/budgetOutputs.js +14 -11
  95. package/dist/tools/schemas/outputs/categoryOutputs.d.ts +1 -1
  96. package/dist/tools/schemas/outputs/categoryOutputs.js +49 -29
  97. package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +1 -1
  98. package/dist/tools/schemas/outputs/comparisonOutputs.js +12 -12
  99. package/dist/tools/schemas/outputs/index.d.ts +14 -14
  100. package/dist/tools/schemas/outputs/index.js +14 -14
  101. package/dist/tools/schemas/outputs/monthOutputs.d.ts +1 -1
  102. package/dist/tools/schemas/outputs/monthOutputs.js +56 -41
  103. package/dist/tools/schemas/outputs/payeeOutputs.d.ts +1 -1
  104. package/dist/tools/schemas/outputs/payeeOutputs.js +10 -10
  105. package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +2 -2
  106. package/dist/tools/schemas/outputs/reconciliationOutputs.js +45 -45
  107. package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +1 -1
  108. package/dist/tools/schemas/outputs/transactionMutationOutputs.js +28 -22
  109. package/dist/tools/schemas/outputs/transactionOutputs.d.ts +1 -1
  110. package/dist/tools/schemas/outputs/transactionOutputs.js +43 -35
  111. package/dist/tools/schemas/outputs/utilityOutputs.d.ts +1 -1
  112. package/dist/tools/schemas/outputs/utilityOutputs.js +5 -3
  113. package/dist/tools/schemas/shared/commonOutputs.d.ts +1 -1
  114. package/dist/tools/schemas/shared/commonOutputs.js +15 -9
  115. package/dist/tools/transactionReadTools.d.ts +11 -0
  116. package/dist/tools/transactionReadTools.js +202 -0
  117. package/dist/tools/transactionSchemas.d.ts +7 -7
  118. package/dist/tools/transactionSchemas.js +77 -57
  119. package/dist/tools/transactionTools.d.ts +6 -24
  120. package/dist/tools/transactionTools.js +7 -1499
  121. package/dist/tools/transactionUtils.d.ts +6 -6
  122. package/dist/tools/transactionUtils.js +78 -63
  123. package/dist/tools/transactionWriteTools.d.ts +20 -0
  124. package/dist/tools/transactionWriteTools.js +1342 -0
  125. package/dist/tools/utilityTools.d.ts +5 -4
  126. package/dist/tools/utilityTools.js +11 -11
  127. package/dist/types/index.d.ts +7 -7
  128. package/dist/types/index.js +6 -6
  129. package/dist/types/reconciliation.d.ts +1 -1
  130. package/dist/types/toolRegistration.d.ts +14 -12
  131. package/dist/utils/amountUtils.js +1 -1
  132. package/dist/utils/dateUtils.js +4 -4
  133. package/dist/utils/errors.d.ts +3 -3
  134. package/dist/utils/errors.js +4 -4
  135. package/dist/utils/money.d.ts +2 -2
  136. package/dist/utils/money.js +8 -8
  137. package/dist/utils/validationError.d.ts +1 -1
  138. package/dist/utils/validationError.js +1 -1
  139. package/docs/assets/examples/reconciliation-with-recommendations.json +66 -66
  140. package/docs/assets/schemas/reconciliation-v2.json +360 -336
  141. package/esbuild.config.mjs +53 -50
  142. package/meta.json +12548 -12548
  143. package/package.json +98 -111
  144. package/scripts/analyze-bundle.mjs +33 -30
  145. package/scripts/create-pr-description.js +169 -120
  146. package/scripts/run-all-tests.js +178 -169
  147. package/scripts/run-domain-integration-tests.js +28 -18
  148. package/scripts/run-generate-mcpb.js +19 -17
  149. package/scripts/run-throttled-integration-tests.js +92 -83
  150. package/scripts/test-delta-params.mjs +149 -120
  151. package/scripts/test-recommendations.ts +36 -32
  152. package/scripts/tmpTransaction.ts +80 -43
  153. package/scripts/validate-env.js +98 -91
  154. package/scripts/verify-build.js +78 -76
  155. package/src/__tests__/comprehensive.integration.test.ts +1281 -1154
  156. package/src/__tests__/performance.test.ts +723 -671
  157. package/src/__tests__/setup.ts +442 -395
  158. package/src/__tests__/smoke.e2e.test.ts +41 -39
  159. package/src/__tests__/testRunner.ts +314 -295
  160. package/src/__tests__/testUtils.ts +456 -364
  161. package/src/__tests__/tools/reconciliation/csvParser.integration.test.ts +109 -107
  162. package/src/__tests__/tools/reconciliation/real-world.integration.test.ts +41 -41
  163. package/src/index.ts +68 -59
  164. package/src/server/CLAUDE.md +480 -0
  165. package/src/server/YNABMCPServer.ts +821 -794
  166. package/src/server/__tests__/YNABMCPServer.integration.test.ts +929 -893
  167. package/src/server/__tests__/YNABMCPServer.test.ts +903 -899
  168. package/src/server/__tests__/budgetResolver.test.ts +466 -423
  169. package/src/server/__tests__/cacheManager.test.ts +891 -874
  170. package/src/server/__tests__/completions.integration.test.ts +115 -106
  171. package/src/server/__tests__/completions.test.ts +334 -313
  172. package/src/server/__tests__/config.test.ts +98 -86
  173. package/src/server/__tests__/deltaCache.merge.test.ts +774 -703
  174. package/src/server/__tests__/deltaCache.swr.test.ts +198 -153
  175. package/src/server/__tests__/deltaCache.test.ts +946 -759
  176. package/src/server/__tests__/diagnostics.test.ts +825 -792
  177. package/src/server/__tests__/errorHandler.integration.test.ts +512 -462
  178. package/src/server/__tests__/errorHandler.test.ts +402 -397
  179. package/src/server/__tests__/prompts.test.ts +424 -347
  180. package/src/server/__tests__/rateLimiter.test.ts +313 -309
  181. package/src/server/__tests__/requestLogger.test.ts +443 -403
  182. package/src/server/__tests__/resources.template.test.ts +196 -185
  183. package/src/server/__tests__/resources.test.ts +294 -288
  184. package/src/server/__tests__/security.integration.test.ts +487 -421
  185. package/src/server/__tests__/securityMiddleware.test.ts +519 -444
  186. package/src/server/__tests__/server-startup.integration.test.ts +509 -490
  187. package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -173
  188. package/src/server/__tests__/toolRegistration.test.ts +239 -210
  189. package/src/server/__tests__/toolRegistry.test.ts +907 -845
  190. package/src/server/budgetResolver.ts +221 -181
  191. package/src/server/cacheKeys.ts +6 -6
  192. package/src/server/cacheManager.ts +498 -484
  193. package/src/server/completions.ts +267 -243
  194. package/src/server/config.ts +35 -14
  195. package/src/server/deltaCache.merge.ts +146 -128
  196. package/src/server/deltaCache.ts +352 -309
  197. package/src/server/diagnostics.ts +257 -242
  198. package/src/server/errorHandler.ts +747 -744
  199. package/src/server/prompts.ts +181 -176
  200. package/src/server/rateLimiter.ts +131 -129
  201. package/src/server/requestLogger.ts +350 -322
  202. package/src/server/resources.ts +442 -374
  203. package/src/server/responseFormatter.ts +41 -37
  204. package/src/server/securityMiddleware.ts +223 -205
  205. package/src/server/serverKnowledgeStore.ts +67 -67
  206. package/src/server/toolRegistry.ts +508 -474
  207. package/src/tools/CLAUDE.md +604 -0
  208. package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -111
  209. package/src/tools/__tests__/accountTools.integration.test.ts +129 -111
  210. package/src/tools/__tests__/accountTools.test.ts +685 -638
  211. package/src/tools/__tests__/adapters.test.ts +142 -108
  212. package/src/tools/__tests__/budgetTools.delta.integration.test.ts +73 -73
  213. package/src/tools/__tests__/budgetTools.integration.test.ts +132 -124
  214. package/src/tools/__tests__/budgetTools.test.ts +442 -413
  215. package/src/tools/__tests__/categoryTools.delta.integration.test.ts +76 -68
  216. package/src/tools/__tests__/categoryTools.integration.test.ts +314 -288
  217. package/src/tools/__tests__/categoryTools.test.ts +656 -625
  218. package/src/tools/__tests__/compareTransactions/formatter.test.ts +535 -462
  219. package/src/tools/__tests__/compareTransactions/index.test.ts +378 -358
  220. package/src/tools/__tests__/compareTransactions/matcher.test.ts +497 -398
  221. package/src/tools/__tests__/compareTransactions/parser.test.ts +765 -747
  222. package/src/tools/__tests__/compareTransactions.test.ts +352 -332
  223. package/src/tools/__tests__/compareTransactions.window.test.ts +150 -146
  224. package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +69 -65
  225. package/src/tools/__tests__/deltaFetcher.test.ts +325 -265
  226. package/src/tools/__tests__/deltaSupport.test.ts +211 -184
  227. package/src/tools/__tests__/deltaTestUtils.ts +37 -33
  228. package/src/tools/__tests__/exportTransactions.test.ts +205 -200
  229. package/src/tools/__tests__/monthTools.delta.integration.test.ts +68 -68
  230. package/src/tools/__tests__/monthTools.integration.test.ts +178 -166
  231. package/src/tools/__tests__/monthTools.test.ts +561 -512
  232. package/src/tools/__tests__/payeeTools.delta.integration.test.ts +68 -68
  233. package/src/tools/__tests__/payeeTools.integration.test.ts +158 -142
  234. package/src/tools/__tests__/payeeTools.test.ts +486 -434
  235. package/src/tools/__tests__/transactionSchemas.test.ts +1202 -1186
  236. package/src/tools/__tests__/transactionTools.integration.test.ts +875 -825
  237. package/src/tools/__tests__/transactionTools.test.ts +4923 -4366
  238. package/src/tools/__tests__/transactionUtils.test.ts +1004 -977
  239. package/src/tools/__tests__/utilityTools.integration.test.ts +32 -32
  240. package/src/tools/__tests__/utilityTools.test.ts +68 -58
  241. package/src/tools/accountTools.ts +293 -271
  242. package/src/tools/adapters.ts +120 -63
  243. package/src/tools/budgetTools.ts +121 -116
  244. package/src/tools/categoryTools.ts +379 -339
  245. package/src/tools/compareTransactions/formatter.ts +131 -119
  246. package/src/tools/compareTransactions/index.ts +249 -214
  247. package/src/tools/compareTransactions/matcher.ts +259 -209
  248. package/src/tools/compareTransactions/parser.ts +517 -487
  249. package/src/tools/compareTransactions/types.ts +38 -38
  250. package/src/tools/compareTransactions.ts +1 -1
  251. package/src/tools/deltaFetcher.ts +281 -260
  252. package/src/tools/deltaSupport.ts +264 -259
  253. package/src/tools/exportTransactions.ts +230 -218
  254. package/src/tools/monthTools.ts +180 -165
  255. package/src/tools/payeeTools.ts +152 -140
  256. package/src/tools/reconcileAdapter.ts +297 -252
  257. package/src/tools/reconciliation/CLAUDE.md +506 -0
  258. package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +133 -124
  259. package/src/tools/reconciliation/__tests__/adapter.test.ts +249 -230
  260. package/src/tools/reconciliation/__tests__/analyzer.test.ts +408 -400
  261. package/src/tools/reconciliation/__tests__/csvParser.test.ts +71 -69
  262. package/src/tools/reconciliation/__tests__/executor.integration.test.ts +348 -323
  263. package/src/tools/reconciliation/__tests__/executor.progress.test.ts +503 -457
  264. package/src/tools/reconciliation/__tests__/executor.test.ts +898 -831
  265. package/src/tools/reconciliation/__tests__/matcher.test.ts +667 -663
  266. package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +296 -276
  267. package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +692 -624
  268. package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1008 -989
  269. package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +187 -146
  270. package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +583 -533
  271. package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +75 -74
  272. package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +70 -62
  273. package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +102 -88
  274. package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +56 -55
  275. package/src/tools/reconciliation/__tests__/signDetector.test.ts +209 -206
  276. package/src/tools/reconciliation/__tests__/ynabAdapter.test.ts +66 -60
  277. package/src/tools/reconciliation/analyzer.ts +564 -504
  278. package/src/tools/reconciliation/csvParser.ts +656 -609
  279. package/src/tools/reconciliation/executor.ts +1290 -1128
  280. package/src/tools/reconciliation/index.ts +580 -528
  281. package/src/tools/reconciliation/matcher.ts +256 -240
  282. package/src/tools/reconciliation/payeeNormalizer.ts +92 -78
  283. package/src/tools/reconciliation/recommendationEngine.ts +357 -345
  284. package/src/tools/reconciliation/reportFormatter.ts +343 -307
  285. package/src/tools/reconciliation/signDetector.ts +89 -83
  286. package/src/tools/reconciliation/types.ts +164 -159
  287. package/src/tools/reconciliation/ynabAdapter.ts +17 -15
  288. package/src/tools/schemas/CLAUDE.md +546 -0
  289. package/src/tools/schemas/common.ts +1 -1
  290. package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +410 -409
  291. package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +305 -299
  292. package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +431 -430
  293. package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +510 -495
  294. package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +179 -153
  295. package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +293 -254
  296. package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +457 -457
  297. package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +362 -356
  298. package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +402 -399
  299. package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +225 -211
  300. package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +457 -454
  301. package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +316 -315
  302. package/src/tools/schemas/outputs/accountOutputs.ts +40 -34
  303. package/src/tools/schemas/outputs/budgetOutputs.ts +24 -19
  304. package/src/tools/schemas/outputs/categoryOutputs.ts +76 -56
  305. package/src/tools/schemas/outputs/comparisonOutputs.ts +192 -169
  306. package/src/tools/schemas/outputs/index.ts +163 -163
  307. package/src/tools/schemas/outputs/monthOutputs.ts +95 -80
  308. package/src/tools/schemas/outputs/payeeOutputs.ts +18 -18
  309. package/src/tools/schemas/outputs/reconciliationOutputs.ts +386 -373
  310. package/src/tools/schemas/outputs/transactionMutationOutputs.ts +259 -231
  311. package/src/tools/schemas/outputs/transactionOutputs.ts +81 -71
  312. package/src/tools/schemas/outputs/utilityOutputs.ts +90 -84
  313. package/src/tools/schemas/shared/commonOutputs.ts +27 -19
  314. package/src/tools/toolCategories.ts +114 -114
  315. package/src/tools/transactionReadTools.ts +327 -0
  316. package/src/tools/transactionSchemas.ts +322 -291
  317. package/src/tools/transactionTools.ts +84 -2246
  318. package/src/tools/transactionUtils.ts +507 -422
  319. package/src/tools/transactionWriteTools.ts +2110 -0
  320. package/src/tools/utilityTools.ts +46 -41
  321. package/src/types/CLAUDE.md +477 -0
  322. package/src/types/__tests__/index.test.ts +51 -51
  323. package/src/types/index.ts +43 -39
  324. package/src/types/integration-tests.d.ts +26 -26
  325. package/src/types/reconciliation.ts +29 -29
  326. package/src/types/toolAnnotations.ts +30 -30
  327. package/src/types/toolRegistration.ts +43 -32
  328. package/src/utils/CLAUDE.md +508 -0
  329. package/src/utils/__tests__/dateUtils.test.ts +174 -168
  330. package/src/utils/__tests__/money.test.ts +193 -187
  331. package/src/utils/amountUtils.ts +5 -5
  332. package/src/utils/baseError.ts +5 -5
  333. package/src/utils/dateUtils.ts +29 -26
  334. package/src/utils/errors.ts +14 -14
  335. package/src/utils/money.ts +66 -52
  336. package/src/utils/validationError.ts +1 -1
  337. package/tsconfig.json +29 -29
  338. package/tsconfig.prod.json +16 -16
  339. package/vitest-reporters/split-json-reporter.ts +247 -204
  340. package/vitest.config.ts +99 -95
  341. package/.prettierignore +0 -10
  342. package/.prettierrc.json +0 -10
  343. package/eslint.config.js +0 -49
@@ -1,7 +1,22 @@
1
+ export var YNABErrorCode;
2
+ (function (YNABErrorCode) {
3
+ YNABErrorCode[YNABErrorCode["BAD_REQUEST"] = 400] = "BAD_REQUEST";
4
+ YNABErrorCode[YNABErrorCode["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
5
+ YNABErrorCode[YNABErrorCode["FORBIDDEN"] = 403] = "FORBIDDEN";
6
+ YNABErrorCode[YNABErrorCode["NOT_FOUND"] = 404] = "NOT_FOUND";
7
+ YNABErrorCode[YNABErrorCode["TOO_MANY_REQUESTS"] = 429] = "TOO_MANY_REQUESTS";
8
+ YNABErrorCode[YNABErrorCode["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
9
+ })(YNABErrorCode || (YNABErrorCode = {}));
10
+ export var SecurityErrorCode;
11
+ (function (SecurityErrorCode) {
12
+ SecurityErrorCode["RATE_LIMIT_EXCEEDED"] = "RATE_LIMIT_EXCEEDED";
13
+ SecurityErrorCode["VALIDATION_ERROR"] = "VALIDATION_ERROR";
14
+ SecurityErrorCode["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
15
+ })(SecurityErrorCode || (SecurityErrorCode = {}));
1
16
  export class YNABAPIError extends Error {
2
17
  constructor(code, message, originalError) {
3
18
  super(message);
4
- this.name = 'YNABAPIError';
19
+ this.name = "YNABAPIError";
5
20
  this.code = code;
6
21
  this.originalError = originalError;
7
22
  }
@@ -12,7 +27,7 @@ export class YNABAPIError extends Error {
12
27
  export class ValidationError extends Error {
13
28
  constructor(message, details, suggestions) {
14
29
  super(message);
15
- this.name = 'ValidationError';
30
+ this.name = "ValidationError";
16
31
  this.details = details;
17
32
  this.suggestions = suggestions;
18
33
  }
@@ -21,14 +36,6 @@ export class ErrorHandler {
21
36
  constructor(formatter) {
22
37
  this.formatter = formatter;
23
38
  }
24
- static createFallbackFormatter() {
25
- return {
26
- format: (value) => JSON.stringify(value, null, 2),
27
- };
28
- }
29
- static setFormatter(formatter) {
30
- ErrorHandler.defaultInstance = new ErrorHandler(formatter);
31
- }
32
39
  handleError(error, context) {
33
40
  const errorResponse = this.createErrorResponse(error, context);
34
41
  let formattedText;
@@ -47,18 +54,12 @@ export class ErrorHandler {
47
54
  isError: true,
48
55
  content: [
49
56
  {
50
- type: 'text',
57
+ type: "text",
51
58
  text: formattedText,
52
59
  },
53
60
  ],
54
61
  };
55
62
  }
56
- static handleError(error, context) {
57
- if (!ErrorHandler.defaultInstance) {
58
- ErrorHandler.defaultInstance = new ErrorHandler(ErrorHandler.createFallbackFormatter());
59
- }
60
- return ErrorHandler.defaultInstance.handleError(error, context);
61
- }
62
63
  createErrorResponse(error, context) {
63
64
  if (error instanceof YNABAPIError) {
64
65
  const ynabDetails = this.extractYNABApiError(error.originalError);
@@ -75,15 +76,17 @@ export class ErrorHandler {
75
76
  };
76
77
  }
77
78
  if (error instanceof ValidationError) {
78
- const sanitizedDetails = error.details ? this.sanitizeErrorDetails(error.details) : undefined;
79
+ const sanitizedDetails = error.details
80
+ ? this.sanitizeErrorDetails(error.details)
81
+ : undefined;
79
82
  const suggestions = error.suggestions && error.suggestions.length > 0
80
83
  ? error.suggestions
81
- : this.getErrorSuggestions("VALIDATION_ERROR", context);
84
+ : this.getErrorSuggestions(SecurityErrorCode.VALIDATION_ERROR, context);
82
85
  return {
83
86
  error: {
84
- code: "VALIDATION_ERROR",
87
+ code: SecurityErrorCode.VALIDATION_ERROR,
85
88
  message: error.message,
86
- userMessage: this.getUserFriendlyMessage("VALIDATION_ERROR", context),
89
+ userMessage: this.getUserFriendlyMessage(SecurityErrorCode.VALIDATION_ERROR, context),
87
90
  suggestions,
88
91
  ...(sanitizedDetails && { details: sanitizedDetails }),
89
92
  },
@@ -137,10 +140,10 @@ export class ErrorHandler {
137
140
  if (error instanceof Error) {
138
141
  errorMessage = error.message;
139
142
  }
140
- else if (typeof error === 'string') {
143
+ else if (typeof error === "string") {
141
144
  errorMessage = error;
142
145
  }
143
- else if (error && typeof error === 'object') {
146
+ else if (error && typeof error === "object") {
144
147
  try {
145
148
  errorMessage = JSON.stringify(error, null, 2);
146
149
  }
@@ -154,13 +157,13 @@ export class ErrorHandler {
154
157
  const sanitizedDetails = this.sanitizeErrorDetails(errorMessage);
155
158
  return {
156
159
  error: {
157
- code: "UNKNOWN_ERROR",
160
+ code: SecurityErrorCode.UNKNOWN_ERROR,
158
161
  message: this.getGenericErrorMessage(context),
159
162
  userMessage: this.getUserFriendlyGenericMessage(context),
160
163
  suggestions: [
161
- 'Try the operation again',
162
- 'Check your internet connection',
163
- 'Contact support if the issue persists',
164
+ "Try the operation again",
165
+ "Check your internet connection",
166
+ "Contact support if the issue persists",
164
167
  ],
165
168
  ...(sanitizedDetails && { details: sanitizedDetails }),
166
169
  },
@@ -168,254 +171,264 @@ export class ErrorHandler {
168
171
  }
169
172
  detectErrorCode(error) {
170
173
  const message = error.message.toLowerCase();
171
- if (message.includes('401') || message.includes('unauthorized')) {
172
- return 401;
174
+ if (message.includes("401") || message.includes("unauthorized")) {
175
+ return YNABErrorCode.UNAUTHORIZED;
173
176
  }
174
- if (message.includes('403') || message.includes('forbidden')) {
175
- return 403;
177
+ if (message.includes("403") || message.includes("forbidden")) {
178
+ return YNABErrorCode.FORBIDDEN;
176
179
  }
177
- if (message.includes('404') || message.includes('not found')) {
178
- return 404;
180
+ if (message.includes("404") || message.includes("not found")) {
181
+ return YNABErrorCode.NOT_FOUND;
179
182
  }
180
- if (message.includes('429') || message.includes('too many requests')) {
181
- return 429;
183
+ if (message.includes("429") || message.includes("too many requests")) {
184
+ return YNABErrorCode.TOO_MANY_REQUESTS;
182
185
  }
183
- if (message.includes('500') || message.includes('internal server error')) {
184
- return 500;
186
+ if (message.includes("500") || message.includes("internal server error")) {
187
+ return YNABErrorCode.INTERNAL_SERVER_ERROR;
185
188
  }
186
189
  return null;
187
190
  }
188
191
  getUserFriendlyMessage(code, context) {
189
192
  switch (code) {
190
- case 400:
191
- return 'The request was invalid. Please check your input data.';
192
- case 401:
193
- return 'Your YNAB access token is invalid or has expired. Please check your token and try again.';
194
- case 403:
193
+ case YNABErrorCode.BAD_REQUEST:
194
+ return "The request was invalid. Please check your input data.";
195
+ case YNABErrorCode.UNAUTHORIZED:
196
+ return "Your YNAB access token is invalid or has expired. Please check your token and try again.";
197
+ case YNABErrorCode.FORBIDDEN:
195
198
  return "You don't have permission to access this YNAB data. Please check your account permissions.";
196
- case 404:
199
+ case YNABErrorCode.NOT_FOUND:
197
200
  return this.getUserFriendlyNotFoundMessage(context);
198
- case 429:
201
+ case YNABErrorCode.TOO_MANY_REQUESTS:
199
202
  return "We're making too many requests to YNAB. Please wait a moment and try again.";
200
- case 500:
203
+ case YNABErrorCode.INTERNAL_SERVER_ERROR:
201
204
  return "YNAB's servers are having issues. Please try again in a few minutes.";
202
- case "VALIDATION_ERROR":
203
- return 'Some of the information provided is invalid. Please check your inputs and try again.';
204
- case "RATE_LIMIT_EXCEEDED":
205
- return 'Too many requests have been made. Please wait before trying again.';
205
+ case SecurityErrorCode.VALIDATION_ERROR:
206
+ return "Some of the information provided is invalid. Please check your inputs and try again.";
207
+ case SecurityErrorCode.RATE_LIMIT_EXCEEDED:
208
+ return "Too many requests have been made. Please wait before trying again.";
206
209
  default:
207
210
  return this.getUserFriendlyGenericMessage(context);
208
211
  }
209
212
  }
210
213
  getErrorSuggestions(code, context) {
211
214
  switch (code) {
212
- case 400:
215
+ case YNABErrorCode.BAD_REQUEST:
213
216
  return [
214
- 'Check that all required fields are correct',
215
- 'Verify that dates are in the correct format (ISO 8601)',
216
- 'Ensure amounts are valid numbers',
217
+ "Check that all required fields are correct",
218
+ "Verify that dates are in the correct format (ISO 8601)",
219
+ "Ensure amounts are valid numbers",
217
220
  ];
218
- case 401:
221
+ case YNABErrorCode.UNAUTHORIZED:
219
222
  return [
220
- 'Go to https://app.youneedabudget.com/settings/developer to generate a new access token',
221
- 'Make sure you copied the entire token without any extra spaces',
223
+ "Go to https://app.youneedabudget.com/settings/developer to generate a new access token",
224
+ "Make sure you copied the entire token without any extra spaces",
222
225
  "Check that your token hasn't expired",
223
226
  ];
224
- case 403:
227
+ case YNABErrorCode.FORBIDDEN:
225
228
  return [
226
- 'Verify that your YNAB account has access to the requested budget',
227
- 'Check if your YNAB subscription is active',
228
- 'Try logging into YNAB directly to confirm access',
229
+ "Verify that your YNAB account has access to the requested budget",
230
+ "Check if your YNAB subscription is active",
231
+ "Try logging into YNAB directly to confirm access",
229
232
  ];
230
- case 404:
233
+ case YNABErrorCode.NOT_FOUND:
231
234
  return this.getNotFoundSuggestions(context);
232
- case 429:
235
+ case YNABErrorCode.TOO_MANY_REQUESTS:
233
236
  return [
234
- 'Wait 1-2 minutes before trying again',
235
- 'Try making fewer requests at once',
236
- 'The system will automatically retry after a short delay',
237
+ "Wait 1-2 minutes before trying again",
238
+ "Try making fewer requests at once",
239
+ "The system will automatically retry after a short delay",
237
240
  ];
238
- case 500:
241
+ case YNABErrorCode.INTERNAL_SERVER_ERROR:
239
242
  return [
240
243
  "Check YNAB's status page at https://status.youneedabudget.com",
241
- 'Try again in a few minutes',
242
- 'Contact YNAB support if the issue persists',
244
+ "Try again in a few minutes",
245
+ "Contact YNAB support if the issue persists",
243
246
  ];
244
- case "VALIDATION_ERROR":
247
+ case SecurityErrorCode.VALIDATION_ERROR:
245
248
  return [
246
- 'Double-check all required fields are filled out',
247
- 'Verify that amounts are in the correct format',
248
- 'Make sure dates are valid and in the right format',
249
+ "Double-check all required fields are filled out",
250
+ "Verify that amounts are in the correct format",
251
+ "Make sure dates are valid and in the right format",
249
252
  ];
250
253
  default:
251
254
  return [
252
- 'Try the operation again',
253
- 'Check your internet connection',
254
- 'Contact support if the issue persists',
255
+ "Try the operation again",
256
+ "Check your internet connection",
257
+ "Contact support if the issue persists",
255
258
  ];
256
259
  }
257
260
  }
258
261
  getUserFriendlyNotFoundMessage(context) {
259
- if (context.includes('account')) {
262
+ if (context.includes("account")) {
260
263
  return "We couldn't find the budget or account you're looking for.";
261
264
  }
262
- if (context.includes('budget')) {
265
+ if (context.includes("budget")) {
263
266
  return "We couldn't find that budget. It may have been deleted or you may not have access.";
264
267
  }
265
- if (context.includes('category')) {
268
+ if (context.includes("category")) {
266
269
  return "We couldn't find that category. It may have been deleted or moved.";
267
270
  }
268
- if (context.includes('transaction')) {
271
+ if (context.includes("transaction")) {
269
272
  return "We couldn't find that transaction. It may have been deleted or moved.";
270
273
  }
271
- if (context.includes('payee')) {
274
+ if (context.includes("payee")) {
272
275
  return "We couldn't find that payee in your budget.";
273
276
  }
274
277
  return "We couldn't find what you're looking for. Please check that all information is correct.";
275
278
  }
276
279
  getNotFoundSuggestions(context) {
277
280
  const baseSuggestions = [
278
- 'Double-check that the name or ID is spelled correctly',
279
- 'Try refreshing your budget data',
281
+ "Double-check that the name or ID is spelled correctly",
282
+ "Try refreshing your budget data",
280
283
  "Make sure you're using the right budget",
281
284
  ];
282
- if (context.includes('account')) {
283
- return [...baseSuggestions, 'Check if the account was recently closed or renamed'];
285
+ if (context.includes("account")) {
286
+ return [
287
+ ...baseSuggestions,
288
+ "Check if the account was recently closed or renamed",
289
+ ];
284
290
  }
285
- if (context.includes('category')) {
291
+ if (context.includes("category")) {
286
292
  return [
287
293
  ...baseSuggestions,
288
- 'Check if the category was deleted or moved to a different group',
294
+ "Check if the category was deleted or moved to a different group",
289
295
  ];
290
296
  }
291
- if (context.includes('transaction')) {
297
+ if (context.includes("transaction")) {
292
298
  return [
293
299
  ...baseSuggestions,
294
- 'Check if the transaction was deleted or is in a different account',
300
+ "Check if the transaction was deleted or is in a different account",
295
301
  ];
296
302
  }
297
303
  return baseSuggestions;
298
304
  }
299
305
  getUserFriendlyGenericMessage(context) {
300
- if (context.includes('transaction')) {
301
- return 'There was a problem with your transaction. Please check your information and try again.';
306
+ if (context.includes("transaction")) {
307
+ return "There was a problem with your transaction. Please check your information and try again.";
302
308
  }
303
- if (context.includes('budget')) {
304
- return 'There was a problem accessing your budget data. Please try again.';
309
+ if (context.includes("budget")) {
310
+ return "There was a problem accessing your budget data. Please try again.";
305
311
  }
306
- if (context.includes('account')) {
307
- return 'There was a problem accessing your account information. Please try again.';
312
+ if (context.includes("account")) {
313
+ return "There was a problem accessing your account information. Please try again.";
308
314
  }
309
- return 'Something went wrong. Please try again in a moment.';
315
+ return "Something went wrong. Please try again in a moment.";
310
316
  }
311
317
  getErrorMessage(code, context) {
312
318
  switch (code) {
313
- case 400:
314
- return 'Bad request - invalid parameters';
315
- case 401:
316
- return 'Invalid or expired YNAB access token';
317
- case 403:
318
- return 'Insufficient permissions to access YNAB data';
319
- case 404:
319
+ case YNABErrorCode.BAD_REQUEST:
320
+ return "Bad request - invalid parameters";
321
+ case YNABErrorCode.UNAUTHORIZED:
322
+ return "Invalid or expired YNAB access token";
323
+ case YNABErrorCode.FORBIDDEN:
324
+ return "Insufficient permissions to access YNAB data";
325
+ case YNABErrorCode.NOT_FOUND:
320
326
  return this.getNotFoundMessage(context);
321
- case 429:
322
- return 'Rate limit exceeded. Please try again later';
323
- case 500:
324
- return 'YNAB service is currently unavailable';
327
+ case YNABErrorCode.TOO_MANY_REQUESTS:
328
+ return "Rate limit exceeded. Please try again later";
329
+ case YNABErrorCode.INTERNAL_SERVER_ERROR:
330
+ return "YNAB service is currently unavailable";
325
331
  default:
326
332
  return this.getGenericErrorMessage(context);
327
333
  }
328
334
  }
329
335
  getNotFoundMessage(context) {
330
- if (context.includes('listing accounts')) {
331
- return 'Failed to list accounts - budget or account not found';
336
+ if (context.includes("listing accounts")) {
337
+ return "Failed to list accounts - budget or account not found";
332
338
  }
333
- if (context.includes('getting account')) {
334
- return 'Failed to get account - budget or account not found';
339
+ if (context.includes("getting account")) {
340
+ return "Failed to get account - budget or account not found";
335
341
  }
336
- if (context.includes('listing budgets') || context.includes('getting budget')) {
337
- return 'Budget not found';
342
+ if (context.includes("listing budgets") ||
343
+ context.includes("getting budget")) {
344
+ return "Budget not found";
338
345
  }
339
- if (context.includes('listing categories') || context.includes('getting category')) {
340
- return 'Budget or category not found';
346
+ if (context.includes("listing categories") ||
347
+ context.includes("getting category")) {
348
+ return "Budget or category not found";
341
349
  }
342
- if (context.includes('listing months') || context.includes('getting month')) {
343
- return 'Budget or month not found';
350
+ if (context.includes("listing months") ||
351
+ context.includes("getting month")) {
352
+ return "Budget or month not found";
344
353
  }
345
- if (context.includes('listing payees') || context.includes('getting payee')) {
346
- return 'Budget or payee not found';
354
+ if (context.includes("listing payees") ||
355
+ context.includes("getting payee")) {
356
+ return "Budget or payee not found";
347
357
  }
348
- if (context.includes('listing transactions') || context.includes('getting transaction')) {
349
- return 'Budget, account, category, or transaction not found';
358
+ if (context.includes("listing transactions") ||
359
+ context.includes("getting transaction")) {
360
+ return "Budget, account, category, or transaction not found";
350
361
  }
351
- return 'The requested resource was not found. Please verify the provided IDs are correct.';
362
+ return "The requested resource was not found. Please verify the provided IDs are correct.";
352
363
  }
353
364
  getGenericErrorMessage(context) {
354
- if (context.includes('listing accounts')) {
355
- return 'Failed to list accounts';
365
+ if (context.includes("listing accounts")) {
366
+ return "Failed to list accounts";
356
367
  }
357
- if (context.includes('getting account')) {
358
- return 'Failed to get account';
368
+ if (context.includes("getting account")) {
369
+ return "Failed to get account";
359
370
  }
360
- if (context.includes('creating account')) {
361
- return 'Failed to create account';
371
+ if (context.includes("creating account")) {
372
+ return "Failed to create account";
362
373
  }
363
- if (context.includes('listing budgets')) {
364
- return 'Failed to list budgets';
374
+ if (context.includes("listing budgets")) {
375
+ return "Failed to list budgets";
365
376
  }
366
- if (context.includes('getting budget')) {
367
- return 'Failed to get budget';
377
+ if (context.includes("getting budget")) {
378
+ return "Failed to get budget";
368
379
  }
369
- if (context.includes('listing categories')) {
370
- return 'Failed to list categories';
380
+ if (context.includes("listing categories")) {
381
+ return "Failed to list categories";
371
382
  }
372
- if (context.includes('getting category')) {
373
- return 'Failed to get category';
383
+ if (context.includes("getting category")) {
384
+ return "Failed to get category";
374
385
  }
375
- if (context.includes('updating category')) {
376
- return 'Failed to update category';
386
+ if (context.includes("updating category")) {
387
+ return "Failed to update category";
377
388
  }
378
- if (context.includes('listing months')) {
379
- return 'Failed to list months';
389
+ if (context.includes("listing months")) {
390
+ return "Failed to list months";
380
391
  }
381
- if (context.includes('getting month')) {
382
- return 'Failed to get month data';
392
+ if (context.includes("getting month")) {
393
+ return "Failed to get month data";
383
394
  }
384
- if (context.includes('listing payees')) {
385
- return 'Failed to list payees';
395
+ if (context.includes("listing payees")) {
396
+ return "Failed to list payees";
386
397
  }
387
- if (context.includes('getting payee')) {
388
- return 'Failed to get payee';
398
+ if (context.includes("getting payee")) {
399
+ return "Failed to get payee";
389
400
  }
390
- if (context.includes('listing transactions')) {
391
- return 'Failed to list transactions';
401
+ if (context.includes("listing transactions")) {
402
+ return "Failed to list transactions";
392
403
  }
393
- if (context.includes('getting transaction')) {
394
- return 'Failed to get transaction';
404
+ if (context.includes("getting transaction")) {
405
+ return "Failed to get transaction";
395
406
  }
396
- if (context.includes('creating transaction')) {
397
- return 'Failed to create transaction';
407
+ if (context.includes("creating transaction")) {
408
+ return "Failed to create transaction";
398
409
  }
399
- if (context.includes('updating transaction')) {
400
- return 'Failed to update transaction';
410
+ if (context.includes("updating transaction")) {
411
+ return "Failed to update transaction";
401
412
  }
402
- if (context.includes('getting user')) {
403
- return 'Failed to get user information';
413
+ if (context.includes("getting user")) {
414
+ return "Failed to get user information";
404
415
  }
405
416
  return `An error occurred while ${context}`;
406
417
  }
407
418
  extractHttpStatus(error) {
408
- if (!error || typeof error !== 'object') {
419
+ if (!error || typeof error !== "object") {
409
420
  return null;
410
421
  }
411
422
  const directStatus = error.status;
412
- if (typeof directStatus === 'number' && Number.isInteger(directStatus) && directStatus > 0) {
423
+ if (typeof directStatus === "number" &&
424
+ Number.isInteger(directStatus) &&
425
+ directStatus > 0) {
413
426
  return directStatus;
414
427
  }
415
428
  const response = error.response;
416
- if (response && typeof response === 'object') {
429
+ if (response && typeof response === "object") {
417
430
  const responseStatus = response.status;
418
- if (typeof responseStatus === 'number' &&
431
+ if (typeof responseStatus === "number" &&
419
432
  Number.isInteger(responseStatus) &&
420
433
  responseStatus > 0) {
421
434
  return responseStatus;
@@ -425,23 +438,23 @@ export class ErrorHandler {
425
438
  }
426
439
  mapHttpStatusToErrorCode(status) {
427
440
  switch (status) {
428
- case 400:
429
- case 401:
430
- case 403:
431
- case 404:
432
- case 429:
433
- case 500:
441
+ case YNABErrorCode.BAD_REQUEST:
442
+ case YNABErrorCode.UNAUTHORIZED:
443
+ case YNABErrorCode.FORBIDDEN:
444
+ case YNABErrorCode.NOT_FOUND:
445
+ case YNABErrorCode.TOO_MANY_REQUESTS:
446
+ case YNABErrorCode.INTERNAL_SERVER_ERROR:
434
447
  return status;
435
448
  default:
436
449
  return null;
437
450
  }
438
451
  }
439
452
  extractHttpStatusDetails(error) {
440
- if (error && typeof error === 'object') {
453
+ if (error && typeof error === "object") {
441
454
  const response = error.response;
442
- if (response && typeof response === 'object') {
455
+ if (response && typeof response === "object") {
443
456
  const statusText = response.statusText;
444
- if (typeof statusText === 'string' && statusText.trim().length > 0) {
457
+ if (typeof statusText === "string" && statusText.trim().length > 0) {
445
458
  return this.sanitizeErrorDetails(statusText);
446
459
  }
447
460
  }
@@ -452,51 +465,53 @@ export class ErrorHandler {
452
465
  return undefined;
453
466
  }
454
467
  extractYNABApiError(error) {
455
- if (!error || typeof error !== 'object') {
468
+ if (!error || typeof error !== "object") {
456
469
  return null;
457
470
  }
458
471
  let payload = error.error;
459
472
  if (!payload) {
460
- const responseData = error.response?.data;
461
- if (responseData && typeof responseData === 'object') {
473
+ const responseData = error.response
474
+ ?.data;
475
+ if (responseData && typeof responseData === "object") {
462
476
  payload = responseData.error;
463
477
  }
464
478
  }
465
- if (!payload || typeof payload !== 'object') {
479
+ if (!payload || typeof payload !== "object") {
466
480
  return null;
467
481
  }
468
482
  const id = payload.id;
469
483
  const name = payload.name;
470
484
  const detail = payload.detail;
471
485
  let code = null;
472
- if (typeof id === 'string') {
473
- const numeric = parseInt(id, 10);
486
+ if (typeof id === "string") {
487
+ const numeric = Number.parseInt(id, 10);
474
488
  if (!Number.isNaN(numeric)) {
475
489
  code = this.mapHttpStatusToErrorCode(numeric);
476
490
  }
477
491
  }
478
- if (!code && typeof name === 'string') {
492
+ if (!code && typeof name === "string") {
479
493
  const normalized = name.toLowerCase();
480
- if (normalized.includes('unauthorized')) {
481
- code = 401;
494
+ if (normalized.includes("unauthorized")) {
495
+ code = YNABErrorCode.UNAUTHORIZED;
482
496
  }
483
- else if (normalized.includes('forbidden')) {
484
- code = 403;
497
+ else if (normalized.includes("forbidden")) {
498
+ code = YNABErrorCode.FORBIDDEN;
485
499
  }
486
- else if (normalized.includes('not_found')) {
487
- code = 404;
500
+ else if (normalized.includes("not_found")) {
501
+ code = YNABErrorCode.NOT_FOUND;
488
502
  }
489
- else if (normalized.includes('too_many_requests') || normalized.includes('rate_limit')) {
490
- code = 429;
503
+ else if (normalized.includes("too_many_requests") ||
504
+ normalized.includes("rate_limit")) {
505
+ code = YNABErrorCode.TOO_MANY_REQUESTS;
491
506
  }
492
- else if (normalized.includes('internal_server_error')) {
493
- code = 500;
507
+ else if (normalized.includes("internal_server_error")) {
508
+ code = YNABErrorCode.INTERNAL_SERVER_ERROR;
494
509
  }
495
510
  }
496
511
  if (!code) {
497
512
  return null;
498
513
  }
499
- const details = typeof detail === 'string' ? detail : undefined;
514
+ const details = typeof detail === "string" ? detail : undefined;
500
515
  const result = { code };
501
516
  if (details !== undefined) {
502
517
  result.details = details;
@@ -506,22 +521,22 @@ export class ErrorHandler {
506
521
  sanitizeErrorDetails(error) {
507
522
  if (!error)
508
523
  return undefined;
509
- let details = '';
524
+ let details = "";
510
525
  if (error instanceof Error) {
511
526
  details = error.message;
512
527
  }
513
- else if (typeof error === 'string') {
528
+ else if (typeof error === "string") {
514
529
  details = error;
515
530
  }
516
531
  else {
517
- details = 'Unknown error details';
532
+ details = "Unknown error details";
518
533
  }
519
534
  details = details
520
- .replace(/token[s]?[:\s=]+([^\s,"']+)/gi, 'token=***')
521
- .replace(/key[s]?[:\s=]+([^\s,"']+)/gi, 'key=***')
522
- .replace(/password[s]?[:\s=]+([^\s,"']+)/gi, 'password=***')
523
- .replace(/authorization[:\s=]+[^\r\n]+/gi, 'authorization=***')
524
- .replace(/\bBearer\s+[A-Za-z0-9._-]+/gi, 'Bearer ***');
535
+ .replace(/token[s]?[:\s=]+([^\s,"']+)/gi, "token=***")
536
+ .replace(/key[s]?[:\s=]+([^\s,"']+)/gi, "key=***")
537
+ .replace(/password[s]?[:\s=]+([^\s,"']+)/gi, "password=***")
538
+ .replace(/authorization[:\s=]+[^\r\n]+/gi, "authorization=***")
539
+ .replace(/\bBearer\s+[A-Za-z0-9._-]+/gi, "Bearer ***");
525
540
  return details;
526
541
  }
527
542
  async withErrorHandling(operation, context) {
@@ -532,38 +547,25 @@ export class ErrorHandler {
532
547
  return this.handleError(error, context);
533
548
  }
534
549
  }
535
- static async withErrorHandling(operation, context) {
536
- if (!ErrorHandler.defaultInstance) {
537
- ErrorHandler.defaultInstance = new ErrorHandler(ErrorHandler.createFallbackFormatter());
538
- }
539
- return ErrorHandler.defaultInstance.withErrorHandling(operation, context);
540
- }
541
550
  createValidationError(message, details, suggestions) {
542
- return this.handleError(new ValidationError(message, details, suggestions), 'validating parameters');
543
- }
544
- static createValidationError(message, details, suggestions) {
545
- if (!ErrorHandler.defaultInstance) {
546
- ErrorHandler.defaultInstance = new ErrorHandler(ErrorHandler.createFallbackFormatter());
547
- }
548
- return ErrorHandler.defaultInstance.createValidationError(message, details, suggestions);
551
+ return this.handleError(new ValidationError(message, details, suggestions), "validating parameters");
549
552
  }
550
553
  createYNABError(code, context, originalError) {
551
554
  const message = this.getErrorMessage(code, context);
552
555
  return new YNABAPIError(code, message, originalError);
553
556
  }
554
- static createYNABError(code, context, originalError) {
555
- if (!ErrorHandler.defaultInstance) {
556
- ErrorHandler.defaultInstance = new ErrorHandler(ErrorHandler.createFallbackFormatter());
557
- }
558
- return ErrorHandler.defaultInstance.createYNABError(code, context, originalError);
559
- }
560
557
  }
561
558
  export function createErrorHandler(formatter) {
562
559
  return new ErrorHandler(formatter);
563
560
  }
564
- export function handleToolError(error, toolName, operation) {
565
- return ErrorHandler.handleError(error, `executing ${toolName} - ${operation}`);
561
+ const fallbackErrorHandler = new ErrorHandler({
562
+ format: (value) => JSON.stringify(value, null, 2),
563
+ });
564
+ export function handleToolError(error, toolName, operation, errorHandler) {
565
+ const eh = errorHandler ?? fallbackErrorHandler;
566
+ return eh.handleError(error, `executing ${toolName} - ${operation}`);
566
567
  }
567
- export async function withToolErrorHandling(operation, toolName, operationName) {
568
- return ErrorHandler.withErrorHandling(operation, `executing ${toolName} - ${operationName}`);
568
+ export async function withToolErrorHandling(operation, toolName, operationName, errorHandler) {
569
+ const eh = errorHandler ?? fallbackErrorHandler;
570
+ return eh.withErrorHandling(operation, `executing ${toolName} - ${operationName}`);
569
571
  }