@dizzlkheinz/ynab-mcpb 0.18.3 → 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 (346) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/CLAUDE.md +87 -8
  3. package/bin/ynab-mcp-server.cjs +2 -2
  4. package/bin/ynab-mcp-server.js +3 -3
  5. package/biome.json +39 -0
  6. package/dist/bundle/index.cjs +67 -67
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.js +27 -27
  9. package/dist/server/YNABMCPServer.d.ts +3 -4
  10. package/dist/server/YNABMCPServer.js +111 -116
  11. package/dist/server/budgetResolver.d.ts +6 -5
  12. package/dist/server/budgetResolver.js +46 -36
  13. package/dist/server/cacheKeys.js +6 -6
  14. package/dist/server/cacheManager.js +14 -11
  15. package/dist/server/completions.d.ts +2 -2
  16. package/dist/server/completions.js +20 -15
  17. package/dist/server/config.d.ts +10 -5
  18. package/dist/server/config.js +24 -7
  19. package/dist/server/deltaCache.d.ts +2 -2
  20. package/dist/server/deltaCache.js +22 -16
  21. package/dist/server/deltaCache.merge.d.ts +2 -2
  22. package/dist/server/diagnostics.d.ts +4 -4
  23. package/dist/server/diagnostics.js +38 -32
  24. package/dist/server/errorHandler.d.ts +5 -12
  25. package/dist/server/errorHandler.js +219 -217
  26. package/dist/server/prompts.d.ts +2 -2
  27. package/dist/server/prompts.js +45 -45
  28. package/dist/server/rateLimiter.js +4 -4
  29. package/dist/server/requestLogger.d.ts +1 -1
  30. package/dist/server/requestLogger.js +40 -35
  31. package/dist/server/resources.d.ts +3 -3
  32. package/dist/server/resources.js +55 -52
  33. package/dist/server/responseFormatter.js +6 -6
  34. package/dist/server/securityMiddleware.d.ts +2 -2
  35. package/dist/server/securityMiddleware.js +22 -20
  36. package/dist/server/serverKnowledgeStore.js +1 -1
  37. package/dist/server/toolRegistry.d.ts +3 -3
  38. package/dist/server/toolRegistry.js +47 -40
  39. package/dist/tools/__tests__/deltaTestUtils.d.ts +3 -3
  40. package/dist/tools/__tests__/deltaTestUtils.js +2 -2
  41. package/dist/tools/accountTools.d.ts +9 -8
  42. package/dist/tools/accountTools.js +47 -47
  43. package/dist/tools/adapters.d.ts +13 -8
  44. package/dist/tools/adapters.js +21 -11
  45. package/dist/tools/budgetTools.d.ts +8 -7
  46. package/dist/tools/budgetTools.js +22 -22
  47. package/dist/tools/categoryTools.d.ts +9 -8
  48. package/dist/tools/categoryTools.js +68 -59
  49. package/dist/tools/compareTransactions/formatter.d.ts +3 -3
  50. package/dist/tools/compareTransactions/formatter.js +9 -9
  51. package/dist/tools/compareTransactions/index.d.ts +6 -6
  52. package/dist/tools/compareTransactions/index.js +58 -43
  53. package/dist/tools/compareTransactions/matcher.d.ts +1 -1
  54. package/dist/tools/compareTransactions/matcher.js +28 -15
  55. package/dist/tools/compareTransactions/parser.d.ts +2 -2
  56. package/dist/tools/compareTransactions/parser.js +144 -138
  57. package/dist/tools/compareTransactions/types.d.ts +4 -4
  58. package/dist/tools/compareTransactions.d.ts +1 -1
  59. package/dist/tools/compareTransactions.js +1 -1
  60. package/dist/tools/deltaFetcher.d.ts +2 -2
  61. package/dist/tools/deltaFetcher.js +16 -15
  62. package/dist/tools/deltaSupport.d.ts +4 -4
  63. package/dist/tools/deltaSupport.js +35 -41
  64. package/dist/tools/exportTransactions.d.ts +5 -4
  65. package/dist/tools/exportTransactions.js +61 -59
  66. package/dist/tools/monthTools.d.ts +7 -6
  67. package/dist/tools/monthTools.js +31 -29
  68. package/dist/tools/payeeTools.d.ts +7 -6
  69. package/dist/tools/payeeTools.js +28 -28
  70. package/dist/tools/reconcileAdapter.d.ts +2 -2
  71. package/dist/tools/reconcileAdapter.js +21 -11
  72. package/dist/tools/reconciliation/analyzer.d.ts +4 -4
  73. package/dist/tools/reconciliation/analyzer.js +136 -57
  74. package/dist/tools/reconciliation/csvParser.d.ts +3 -3
  75. package/dist/tools/reconciliation/csvParser.js +128 -104
  76. package/dist/tools/reconciliation/executor.d.ts +4 -4
  77. package/dist/tools/reconciliation/executor.js +148 -109
  78. package/dist/tools/reconciliation/index.d.ts +10 -10
  79. package/dist/tools/reconciliation/index.js +96 -83
  80. package/dist/tools/reconciliation/matcher.d.ts +3 -3
  81. package/dist/tools/reconciliation/matcher.js +17 -16
  82. package/dist/tools/reconciliation/payeeNormalizer.js +19 -8
  83. package/dist/tools/reconciliation/recommendationEngine.d.ts +1 -1
  84. package/dist/tools/reconciliation/recommendationEngine.js +40 -40
  85. package/dist/tools/reconciliation/reportFormatter.d.ts +2 -2
  86. package/dist/tools/reconciliation/reportFormatter.js +79 -54
  87. package/dist/tools/reconciliation/signDetector.d.ts +1 -1
  88. package/dist/tools/reconciliation/types.d.ts +19 -16
  89. package/dist/tools/reconciliation/ynabAdapter.d.ts +2 -2
  90. package/dist/tools/schemas/common.d.ts +1 -1
  91. package/dist/tools/schemas/common.js +1 -1
  92. package/dist/tools/schemas/outputs/accountOutputs.d.ts +1 -1
  93. package/dist/tools/schemas/outputs/accountOutputs.js +24 -18
  94. package/dist/tools/schemas/outputs/budgetOutputs.d.ts +1 -1
  95. package/dist/tools/schemas/outputs/budgetOutputs.js +14 -11
  96. package/dist/tools/schemas/outputs/categoryOutputs.d.ts +1 -1
  97. package/dist/tools/schemas/outputs/categoryOutputs.js +49 -29
  98. package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +1 -1
  99. package/dist/tools/schemas/outputs/comparisonOutputs.js +12 -12
  100. package/dist/tools/schemas/outputs/index.d.ts +14 -14
  101. package/dist/tools/schemas/outputs/index.js +14 -14
  102. package/dist/tools/schemas/outputs/monthOutputs.d.ts +1 -1
  103. package/dist/tools/schemas/outputs/monthOutputs.js +56 -41
  104. package/dist/tools/schemas/outputs/payeeOutputs.d.ts +1 -1
  105. package/dist/tools/schemas/outputs/payeeOutputs.js +10 -10
  106. package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +2 -2
  107. package/dist/tools/schemas/outputs/reconciliationOutputs.js +45 -45
  108. package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +1 -1
  109. package/dist/tools/schemas/outputs/transactionMutationOutputs.js +28 -22
  110. package/dist/tools/schemas/outputs/transactionOutputs.d.ts +1 -1
  111. package/dist/tools/schemas/outputs/transactionOutputs.js +43 -35
  112. package/dist/tools/schemas/outputs/utilityOutputs.d.ts +1 -1
  113. package/dist/tools/schemas/outputs/utilityOutputs.js +5 -3
  114. package/dist/tools/schemas/shared/commonOutputs.d.ts +1 -1
  115. package/dist/tools/schemas/shared/commonOutputs.js +15 -9
  116. package/dist/tools/transactionReadTools.d.ts +11 -0
  117. package/dist/tools/transactionReadTools.js +202 -0
  118. package/dist/tools/transactionSchemas.d.ts +309 -0
  119. package/dist/tools/transactionSchemas.js +235 -0
  120. package/dist/tools/transactionTools.d.ts +6 -302
  121. package/dist/tools/transactionTools.js +7 -2054
  122. package/dist/tools/transactionUtils.d.ts +31 -0
  123. package/dist/tools/transactionUtils.js +364 -0
  124. package/dist/tools/transactionWriteTools.d.ts +20 -0
  125. package/dist/tools/transactionWriteTools.js +1342 -0
  126. package/dist/tools/utilityTools.d.ts +5 -4
  127. package/dist/tools/utilityTools.js +11 -11
  128. package/dist/types/index.d.ts +7 -7
  129. package/dist/types/index.js +6 -6
  130. package/dist/types/reconciliation.d.ts +1 -1
  131. package/dist/types/toolRegistration.d.ts +14 -12
  132. package/dist/utils/amountUtils.js +1 -1
  133. package/dist/utils/dateUtils.js +4 -4
  134. package/dist/utils/errors.d.ts +3 -3
  135. package/dist/utils/errors.js +4 -4
  136. package/dist/utils/money.d.ts +2 -2
  137. package/dist/utils/money.js +8 -8
  138. package/dist/utils/validationError.d.ts +1 -1
  139. package/dist/utils/validationError.js +1 -1
  140. package/docs/assets/examples/reconciliation-with-recommendations.json +66 -66
  141. package/docs/assets/schemas/reconciliation-v2.json +360 -336
  142. package/docs/plans/2025-12-25-transaction-tools-refactor-design.md +211 -0
  143. package/docs/plans/2025-12-25-transaction-tools-refactor.md +905 -0
  144. package/esbuild.config.mjs +53 -50
  145. package/meta.json +12548 -12548
  146. package/package.json +98 -109
  147. package/scripts/analyze-bundle.mjs +33 -30
  148. package/scripts/create-pr-description.js +169 -120
  149. package/scripts/run-all-tests.js +205 -0
  150. package/scripts/run-domain-integration-tests.js +28 -18
  151. package/scripts/run-generate-mcpb.js +19 -17
  152. package/scripts/run-throttled-integration-tests.js +92 -83
  153. package/scripts/test-delta-params.mjs +149 -120
  154. package/scripts/test-recommendations.ts +36 -32
  155. package/scripts/tmpTransaction.ts +80 -43
  156. package/scripts/validate-env.js +98 -91
  157. package/scripts/verify-build.js +78 -76
  158. package/src/__tests__/comprehensive.integration.test.ts +1281 -1154
  159. package/src/__tests__/performance.test.ts +723 -671
  160. package/src/__tests__/setup.ts +442 -395
  161. package/src/__tests__/smoke.e2e.test.ts +41 -39
  162. package/src/__tests__/testRunner.ts +314 -295
  163. package/src/__tests__/testUtils.ts +456 -364
  164. package/src/__tests__/tools/reconciliation/csvParser.integration.test.ts +109 -107
  165. package/src/__tests__/tools/reconciliation/real-world.integration.test.ts +41 -41
  166. package/src/index.ts +68 -59
  167. package/src/server/CLAUDE.md +480 -0
  168. package/src/server/YNABMCPServer.ts +821 -794
  169. package/src/server/__tests__/YNABMCPServer.integration.test.ts +929 -893
  170. package/src/server/__tests__/YNABMCPServer.test.ts +903 -899
  171. package/src/server/__tests__/budgetResolver.test.ts +466 -423
  172. package/src/server/__tests__/cacheManager.test.ts +891 -874
  173. package/src/server/__tests__/completions.integration.test.ts +115 -106
  174. package/src/server/__tests__/completions.test.ts +334 -313
  175. package/src/server/__tests__/config.test.ts +98 -86
  176. package/src/server/__tests__/deltaCache.merge.test.ts +774 -703
  177. package/src/server/__tests__/deltaCache.swr.test.ts +198 -153
  178. package/src/server/__tests__/deltaCache.test.ts +946 -759
  179. package/src/server/__tests__/diagnostics.test.ts +825 -792
  180. package/src/server/__tests__/errorHandler.integration.test.ts +512 -462
  181. package/src/server/__tests__/errorHandler.test.ts +402 -397
  182. package/src/server/__tests__/prompts.test.ts +424 -347
  183. package/src/server/__tests__/rateLimiter.test.ts +313 -309
  184. package/src/server/__tests__/requestLogger.test.ts +443 -403
  185. package/src/server/__tests__/resources.template.test.ts +196 -185
  186. package/src/server/__tests__/resources.test.ts +294 -288
  187. package/src/server/__tests__/security.integration.test.ts +487 -421
  188. package/src/server/__tests__/securityMiddleware.test.ts +519 -444
  189. package/src/server/__tests__/server-startup.integration.test.ts +509 -490
  190. package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -173
  191. package/src/server/__tests__/toolRegistration.test.ts +239 -210
  192. package/src/server/__tests__/toolRegistry.test.ts +907 -845
  193. package/src/server/budgetResolver.ts +221 -181
  194. package/src/server/cacheKeys.ts +6 -6
  195. package/src/server/cacheManager.ts +498 -484
  196. package/src/server/completions.ts +267 -243
  197. package/src/server/config.ts +35 -14
  198. package/src/server/deltaCache.merge.ts +146 -128
  199. package/src/server/deltaCache.ts +352 -309
  200. package/src/server/diagnostics.ts +257 -242
  201. package/src/server/errorHandler.ts +747 -744
  202. package/src/server/prompts.ts +181 -176
  203. package/src/server/rateLimiter.ts +131 -129
  204. package/src/server/requestLogger.ts +350 -322
  205. package/src/server/resources.ts +442 -374
  206. package/src/server/responseFormatter.ts +41 -37
  207. package/src/server/securityMiddleware.ts +223 -205
  208. package/src/server/serverKnowledgeStore.ts +67 -67
  209. package/src/server/toolRegistry.ts +508 -474
  210. package/src/tools/CLAUDE.md +604 -0
  211. package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -111
  212. package/src/tools/__tests__/accountTools.integration.test.ts +129 -111
  213. package/src/tools/__tests__/accountTools.test.ts +685 -638
  214. package/src/tools/__tests__/adapters.test.ts +142 -108
  215. package/src/tools/__tests__/budgetTools.delta.integration.test.ts +73 -73
  216. package/src/tools/__tests__/budgetTools.integration.test.ts +132 -124
  217. package/src/tools/__tests__/budgetTools.test.ts +442 -413
  218. package/src/tools/__tests__/categoryTools.delta.integration.test.ts +76 -68
  219. package/src/tools/__tests__/categoryTools.integration.test.ts +314 -288
  220. package/src/tools/__tests__/categoryTools.test.ts +656 -625
  221. package/src/tools/__tests__/compareTransactions/formatter.test.ts +535 -462
  222. package/src/tools/__tests__/compareTransactions/index.test.ts +378 -358
  223. package/src/tools/__tests__/compareTransactions/matcher.test.ts +497 -398
  224. package/src/tools/__tests__/compareTransactions/parser.test.ts +765 -747
  225. package/src/tools/__tests__/compareTransactions.test.ts +352 -332
  226. package/src/tools/__tests__/compareTransactions.window.test.ts +150 -146
  227. package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +69 -65
  228. package/src/tools/__tests__/deltaFetcher.test.ts +325 -265
  229. package/src/tools/__tests__/deltaSupport.test.ts +211 -184
  230. package/src/tools/__tests__/deltaTestUtils.ts +37 -33
  231. package/src/tools/__tests__/exportTransactions.test.ts +205 -200
  232. package/src/tools/__tests__/monthTools.delta.integration.test.ts +68 -68
  233. package/src/tools/__tests__/monthTools.integration.test.ts +178 -166
  234. package/src/tools/__tests__/monthTools.test.ts +561 -512
  235. package/src/tools/__tests__/payeeTools.delta.integration.test.ts +68 -68
  236. package/src/tools/__tests__/payeeTools.integration.test.ts +158 -142
  237. package/src/tools/__tests__/payeeTools.test.ts +486 -434
  238. package/src/tools/__tests__/transactionSchemas.test.ts +1204 -0
  239. package/src/tools/__tests__/transactionTools.integration.test.ts +875 -825
  240. package/src/tools/__tests__/transactionTools.test.ts +4923 -4366
  241. package/src/tools/__tests__/transactionUtils.test.ts +1016 -0
  242. package/src/tools/__tests__/utilityTools.integration.test.ts +32 -32
  243. package/src/tools/__tests__/utilityTools.test.ts +68 -58
  244. package/src/tools/accountTools.ts +293 -271
  245. package/src/tools/adapters.ts +120 -63
  246. package/src/tools/budgetTools.ts +121 -116
  247. package/src/tools/categoryTools.ts +379 -339
  248. package/src/tools/compareTransactions/formatter.ts +131 -119
  249. package/src/tools/compareTransactions/index.ts +249 -214
  250. package/src/tools/compareTransactions/matcher.ts +259 -209
  251. package/src/tools/compareTransactions/parser.ts +517 -487
  252. package/src/tools/compareTransactions/types.ts +38 -38
  253. package/src/tools/compareTransactions.ts +1 -1
  254. package/src/tools/deltaFetcher.ts +281 -260
  255. package/src/tools/deltaSupport.ts +264 -259
  256. package/src/tools/exportTransactions.ts +230 -218
  257. package/src/tools/monthTools.ts +180 -165
  258. package/src/tools/payeeTools.ts +152 -140
  259. package/src/tools/reconcileAdapter.ts +297 -246
  260. package/src/tools/reconciliation/CLAUDE.md +506 -0
  261. package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +135 -112
  262. package/src/tools/reconciliation/__tests__/adapter.test.ts +249 -227
  263. package/src/tools/reconciliation/__tests__/analyzer.test.ts +408 -335
  264. package/src/tools/reconciliation/__tests__/csvParser.test.ts +71 -69
  265. package/src/tools/reconciliation/__tests__/executor.integration.test.ts +348 -323
  266. package/src/tools/reconciliation/__tests__/executor.progress.test.ts +503 -457
  267. package/src/tools/reconciliation/__tests__/executor.test.ts +898 -831
  268. package/src/tools/reconciliation/__tests__/matcher.test.ts +667 -663
  269. package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +296 -276
  270. package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +692 -624
  271. package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1008 -986
  272. package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +187 -146
  273. package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +583 -530
  274. package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +75 -71
  275. package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +70 -58
  276. package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +102 -88
  277. package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +58 -43
  278. package/src/tools/reconciliation/__tests__/signDetector.test.ts +209 -206
  279. package/src/tools/reconciliation/__tests__/ynabAdapter.test.ts +66 -60
  280. package/src/tools/reconciliation/analyzer.ts +582 -406
  281. package/src/tools/reconciliation/csvParser.ts +656 -609
  282. package/src/tools/reconciliation/executor.ts +1290 -1128
  283. package/src/tools/reconciliation/index.ts +580 -528
  284. package/src/tools/reconciliation/matcher.ts +256 -240
  285. package/src/tools/reconciliation/payeeNormalizer.ts +92 -78
  286. package/src/tools/reconciliation/recommendationEngine.ts +357 -345
  287. package/src/tools/reconciliation/reportFormatter.ts +349 -276
  288. package/src/tools/reconciliation/signDetector.ts +89 -83
  289. package/src/tools/reconciliation/types.ts +164 -153
  290. package/src/tools/reconciliation/ynabAdapter.ts +17 -15
  291. package/src/tools/schemas/CLAUDE.md +546 -0
  292. package/src/tools/schemas/common.ts +1 -1
  293. package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +410 -409
  294. package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +305 -299
  295. package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +431 -430
  296. package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +510 -495
  297. package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +179 -153
  298. package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +293 -254
  299. package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +457 -457
  300. package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +362 -356
  301. package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +402 -399
  302. package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +225 -211
  303. package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +457 -454
  304. package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +316 -315
  305. package/src/tools/schemas/outputs/accountOutputs.ts +40 -34
  306. package/src/tools/schemas/outputs/budgetOutputs.ts +24 -19
  307. package/src/tools/schemas/outputs/categoryOutputs.ts +76 -56
  308. package/src/tools/schemas/outputs/comparisonOutputs.ts +192 -169
  309. package/src/tools/schemas/outputs/index.ts +163 -163
  310. package/src/tools/schemas/outputs/monthOutputs.ts +95 -80
  311. package/src/tools/schemas/outputs/payeeOutputs.ts +18 -18
  312. package/src/tools/schemas/outputs/reconciliationOutputs.ts +386 -373
  313. package/src/tools/schemas/outputs/transactionMutationOutputs.ts +259 -231
  314. package/src/tools/schemas/outputs/transactionOutputs.ts +81 -71
  315. package/src/tools/schemas/outputs/utilityOutputs.ts +90 -84
  316. package/src/tools/schemas/shared/commonOutputs.ts +27 -19
  317. package/src/tools/toolCategories.ts +114 -114
  318. package/src/tools/transactionReadTools.ts +327 -0
  319. package/src/tools/transactionSchemas.ts +484 -0
  320. package/src/tools/transactionTools.ts +107 -2990
  321. package/src/tools/transactionUtils.ts +621 -0
  322. package/src/tools/transactionWriteTools.ts +2110 -0
  323. package/src/tools/utilityTools.ts +46 -41
  324. package/src/types/CLAUDE.md +477 -0
  325. package/src/types/__tests__/index.test.ts +51 -51
  326. package/src/types/index.ts +43 -39
  327. package/src/types/integration-tests.d.ts +26 -26
  328. package/src/types/reconciliation.ts +29 -29
  329. package/src/types/toolAnnotations.ts +30 -30
  330. package/src/types/toolRegistration.ts +43 -32
  331. package/src/utils/CLAUDE.md +508 -0
  332. package/src/utils/__tests__/dateUtils.test.ts +174 -168
  333. package/src/utils/__tests__/money.test.ts +193 -187
  334. package/src/utils/amountUtils.ts +5 -5
  335. package/src/utils/baseError.ts +5 -5
  336. package/src/utils/dateUtils.ts +29 -26
  337. package/src/utils/errors.ts +14 -14
  338. package/src/utils/money.ts +66 -52
  339. package/src/utils/validationError.ts +1 -1
  340. package/tsconfig.json +29 -29
  341. package/tsconfig.prod.json +16 -16
  342. package/vitest-reporters/split-json-reporter.ts +247 -204
  343. package/vitest.config.ts +99 -95
  344. package/.prettierignore +0 -10
  345. package/.prettierrc.json +0 -10
  346. 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
  }