@dizzlkheinz/ynab-mcpb 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (435) hide show
  1. package/.chunkhound.json +11 -0
  2. package/.code/agents/0427d95e-edca-431f-a214-5e53264e29c4/error.txt +8 -0
  3. package/.code/agents/0d675174-d1e1-41c3-9975-4c2e275819a9/error.txt +3 -0
  4. package/.code/agents/0d8c5afd-4787-422b-abf8-2e5943fc7e67/error.txt +3 -0
  5. package/.code/agents/0ec34a70-ed5d-4b9e-bee4-bb0e4cccbc4b/error.txt +1 -0
  6. package/.code/agents/0ef51a21-1ab1-49d7-9561-0eaa43875ebc/error.txt +12 -0
  7. package/.code/agents/15db95d7-abad-4b4d-9c3b-8446089cb61d/error.txt +1 -0
  8. package/.code/agents/19ab9acb-f675-4ff0-902a-09a5476f8149/error.txt +1 -0
  9. package/.code/agents/1ef7e12d-f6ff-4897-8a9b-152d523d898e/error.txt +5 -0
  10. package/.code/agents/2465/exec-call_lroN9KKzJVWC7t5423DK1nT9.txt +1453 -0
  11. package/.code/agents/28edb6fe-95a9-41a0-ae69-aa0100d26c0c/error.txt +8 -0
  12. package/.code/agents/2ae40cf5-b4bf-42e2-92bf-7ea350a7755e/error.txt +9 -0
  13. package/.code/agents/2bfc4e1f-ac4b-45a5-b6df-bf89d4dbb54c/error.txt +1 -0
  14. package/.code/agents/2e2e1134-eff0-49be-ba25-8e2c3468a564/error.txt +5 -0
  15. package/.code/agents/3/exec-call_203OC4TNVkLxW7z2HCVEQ1cM.txt +81 -0
  16. package/.code/agents/3/exec-call_SS5T0XSiXB4LSNzUKTl75wkh.txt +610 -0
  17. package/.code/agents/3322c003-ce5e-48e3-a342-f5049c5bf9a2/error.txt +1 -0
  18. package/.code/agents/391e9b08-1ebc-468c-9bcd-6d0cc3193b37/error.txt +1 -0
  19. package/.code/agents/3ab0aa84-b7bb-4054-afa3-40b8fd7d3be0/error.txt +1 -0
  20. package/.code/agents/3bed368d-50fe-477e-aee3-a6707eaa1ab9/error.txt +3 -0
  21. package/.code/agents/3e40b925-db12-442f-8d7a-a25fc69a6672/error.txt +8 -0
  22. package/.code/agents/414d5776-cf58-41f3-9328-a6daed503a50/error.txt +5 -0
  23. package/.code/agents/42687751-4565-4610-b240-67835b17d861/error.txt +1 -0
  24. package/.code/agents/46b98876-1a39-43c9-9e2f-507ca6d47335/error.txt +9 -0
  25. package/.code/agents/4a7d9491-b26f-43dd-850d-2ecdc49b5d1b/error.txt +1 -0
  26. package/.code/agents/4e60f00a-1b3e-447f-87f3-7faf9deddec3/error.txt +13 -0
  27. package/.code/agents/5138fc1c-4d49-4b74-a7da-ccdb3a8e44e7/error.txt +14 -0
  28. package/.code/agents/521cff39-a7a3-42e5-a557-134f0f7daaa0/error.txt +5 -0
  29. package/.code/agents/53302dc5-3857-4413-9a47-9e0f64a51dc4/error.txt +5 -0
  30. package/.code/agents/567c7c2e-6a6f-4761-a08d-d36deeb2e0ac/error.txt +5 -0
  31. package/.code/agents/57b00845-80dc-47c9-953c-3028d16275d6/error.txt +3 -0
  32. package/.code/agents/593d9005-c2a5-48fd-8813-ece0d3f2de96/error.txt +1 -0
  33. package/.code/agents/5a112e66-0e1a-42f9-877c-53af56ea3551/error.txt +1 -0
  34. package/.code/agents/5b05e8ed-7788-4738-b7ee-9faa8180f992/error.txt +5 -0
  35. package/.code/agents/5f888d6f-d7ca-4ac8-be23-9ea1bf753951/error.txt +5 -0
  36. package/.code/agents/607db3ab-e4b0-435b-b497-93e9aa525549/error.txt +8 -0
  37. package/.code/agents/67dcb2a2-900f-4c78-b3fc-80b5213e0ddf/error.txt +8 -0
  38. package/.code/agents/69ad848c-4e98-49b3-b16c-0094ac2d1759/error.txt +5 -0
  39. package/.code/agents/6c9cfc5f-0d0b-445c-b121-9f60082c4f70/error.txt +1 -0
  40. package/.code/agents/6f6f8f77-4ab0-4f6e-9f30-40e8be0bd8f5/error.txt +1 -0
  41. package/.code/agents/72a7cde4-fa8a-4024-9038-27faa550539b/error.txt +1 -0
  42. package/.code/agents/7b48335c-8247-43aa-9949-5f820ba8e199/error.txt +1 -0
  43. package/.code/agents/80944249-bea9-4ac5-87de-a666c4df306e/error.txt +1 -0
  44. package/.code/agents/826099df-1b66-4186-a915-7eb59f9db19d/error.txt +5 -0
  45. package/.code/agents/8291d158-18a8-4a92-b799-4e9a4d9cce88/error.txt +1 -0
  46. package/.code/agents/82fb71a3-20fb-4341-804a-a2fc900f95bc/error.txt +1 -0
  47. package/.code/agents/855790ea-54ee-43e4-8209-a66994e37590/error.txt +1 -0
  48. package/.code/agents/88ce3a2e-04f2-42be-9062-bf97aa798da0/error.txt +3 -0
  49. package/.code/agents/9a17e398-b6ed-4218-bb55-bc64a8d38ce8/error.txt +8 -0
  50. package/.code/agents/9a4f4bfc-a2a6-4f40-a896-9335b41a7ed1/error.txt +1 -0
  51. package/.code/agents/9b633e55-ef84-47d6-94bb-fd3dd172ad97/error.txt +1 -0
  52. package/.code/agents/9b81f3ab-c72b-4a81-9a8f-28a49ddba84a/error.txt +8 -0
  53. package/.code/agents/a35daf29-b2d1-4aef-9b42-dad63a76bd47/error.txt +3 -0
  54. package/.code/agents/a81990cc-69ee-44d2-b907-17403c9bc5d7/error.txt +5 -0
  55. package/.code/agents/ab56260a-4a83-4ad4-9410-f88a23d6520a/error.txt +1 -0
  56. package/.code/agents/ad722c31-2d1d-45f7-bae2-3f02ca455b60/error.txt +1 -0
  57. package/.code/agents/b62e8690-3324-4b97-9309-731bee79416b/error.txt +5 -0
  58. package/.code/agents/baf60a3a-752b-4ad8-99d6-df32423ed2eb/error.txt +1 -0
  59. package/.code/agents/be049042-7dcb-4ac8-9beb-c8f1aea67742/error.txt +14 -0
  60. package/.code/agents/bed1dcb4-bfce-4a9f-8594-0f994962aafd/error.txt +1 -0
  61. package/.code/agents/c324a6cf-e935-4ede-9529-b3ebc18e8d6b/error.txt +5 -0
  62. package/.code/agents/c37c06ff-dfe3-43f2-9bbc-3ec73ec8f41d/error.txt +5 -0
  63. package/.code/agents/c8cd6671-433a-456b-9f88-e51cb2df6bfc/error.txt +11 -0
  64. package/.code/agents/ca2ccb67-2f24-428e-b27d-9365beadd140/error.txt +1 -0
  65. package/.code/agents/cf08c0c8-e7f0-423e-93ba-547e8e818340/error.txt +8 -0
  66. package/.code/agents/d579c74f-874b-40a4-9d56-ced1eb6a701d/error.txt +1 -0
  67. package/.code/agents/df412c98-7378-4deb-8e1e-76c416931181/error.txt +3 -0
  68. package/.code/agents/e5134eb3-2af4-45b0-8998-051cb4afdb45/error.txt +3 -0
  69. package/.code/agents/e6308471-aa45-4e9e-9496-2e9404164d97/error.txt +8 -0
  70. package/.code/agents/e7bd8bc7-23fb-4f46-98dc-b0dcf11b75a1/error.txt +1 -0
  71. package/.code/agents/e92bec35-378d-4fe1-8ac0-6e1bb3c86911/error.txt +5 -0
  72. package/.code/agents/ed918fbf-2dc4-4aa2-bfc5-04b65d9471ea/error.txt +1 -0
  73. package/.code/agents/ef1d756f-b272-48fc-8729-f05c494674f7/error.txt +1 -0
  74. package/.code/agents/ef359853-0249-4e41-a804-c0fc459fe456/error.txt +1 -0
  75. package/.code/agents/effc7b4a-4b90-40a0-8c86-a7a99d2d5fd2/error.txt +1 -0
  76. package/.code/agents/fa15f8d5-8359-4a8b-83a3-2f2056b3ff40/error.txt +3 -0
  77. package/.code/agents/fbef4193-eadf-4c8a-83ff-4878a6310f25/error.txt +8 -0
  78. package/.code/agents/fd0a4b4a-fda4-4964-a6d6-2b8a2da387c6/error.txt +1 -0
  79. package/.dxtignore +57 -0
  80. package/.env.example +44 -0
  81. package/.gemini/settings.json +8 -0
  82. package/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
  83. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  84. package/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  85. package/.github/ISSUE_TEMPLATE/release_checklist.md +31 -0
  86. package/.github/pull_request_template.md +41 -0
  87. package/.github/workflows/ci-tests.yml +41 -0
  88. package/.github/workflows/claude-code-review.yml +57 -0
  89. package/.github/workflows/claude.yml +50 -0
  90. package/.github/workflows/full-integration.yml +22 -0
  91. package/.github/workflows/pr-description-check.yml +88 -0
  92. package/.github/workflows/publish.yml +33 -0
  93. package/.github/workflows/release.yml +89 -0
  94. package/.mcpbignore +58 -0
  95. package/.prettierignore +10 -0
  96. package/.prettierrc.json +10 -0
  97. package/ADOS-2-Module-1-Complete-Manual.md +757 -0
  98. package/AGENTS.md +36 -0
  99. package/CHANGELOG.md +187 -0
  100. package/CLAUDE.md +414 -0
  101. package/CODEREVIEW_RESPONSE.md +128 -0
  102. package/LICENSE +17 -0
  103. package/NUL +1 -0
  104. package/README.md +222 -0
  105. package/SCHEMA_IMPROVEMENT_SUMMARY.md +120 -0
  106. package/TESTING_NOTES.md +217 -0
  107. package/WARP.md +245 -0
  108. package/accountactivity-merged.csv +149 -0
  109. package/bin/ynab-mcp-server.cjs +4 -0
  110. package/bin/ynab-mcp-server.js +8 -0
  111. package/bundle-analysis.html +13110 -0
  112. package/dist/bundle/index.cjs +124 -0
  113. package/dist/index.d.ts +2 -0
  114. package/dist/index.js +85 -0
  115. package/dist/server/YNABMCPServer.d.ts +264 -0
  116. package/dist/server/YNABMCPServer.js +845 -0
  117. package/dist/server/budgetResolver.d.ts +15 -0
  118. package/dist/server/budgetResolver.js +99 -0
  119. package/dist/server/cacheManager.d.ts +74 -0
  120. package/dist/server/cacheManager.js +306 -0
  121. package/dist/server/config.d.ts +3 -0
  122. package/dist/server/config.js +19 -0
  123. package/dist/server/deltaCache.d.ts +61 -0
  124. package/dist/server/deltaCache.js +206 -0
  125. package/dist/server/deltaCache.merge.d.ts +9 -0
  126. package/dist/server/deltaCache.merge.js +111 -0
  127. package/dist/server/diagnostics.d.ts +90 -0
  128. package/dist/server/diagnostics.js +163 -0
  129. package/dist/server/errorHandler.d.ts +69 -0
  130. package/dist/server/errorHandler.js +524 -0
  131. package/dist/server/prompts.d.ts +31 -0
  132. package/dist/server/prompts.js +205 -0
  133. package/dist/server/rateLimiter.d.ts +27 -0
  134. package/dist/server/rateLimiter.js +82 -0
  135. package/dist/server/requestLogger.d.ts +62 -0
  136. package/dist/server/requestLogger.js +190 -0
  137. package/dist/server/resources.d.ts +39 -0
  138. package/dist/server/resources.js +85 -0
  139. package/dist/server/responseFormatter.d.ts +14 -0
  140. package/dist/server/responseFormatter.js +42 -0
  141. package/dist/server/securityMiddleware.d.ts +87 -0
  142. package/dist/server/securityMiddleware.js +117 -0
  143. package/dist/server/serverKnowledgeStore.d.ts +11 -0
  144. package/dist/server/serverKnowledgeStore.js +42 -0
  145. package/dist/server/toolRegistry.d.ts +85 -0
  146. package/dist/server/toolRegistry.js +272 -0
  147. package/dist/tools/__tests__/deltaTestUtils.d.ts +18 -0
  148. package/dist/tools/__tests__/deltaTestUtils.js +26 -0
  149. package/dist/tools/accountTools.d.ts +37 -0
  150. package/dist/tools/accountTools.js +175 -0
  151. package/dist/tools/budgetTools.d.ts +10 -0
  152. package/dist/tools/budgetTools.js +68 -0
  153. package/dist/tools/categoryTools.d.ts +27 -0
  154. package/dist/tools/categoryTools.js +232 -0
  155. package/dist/tools/compareTransactions/formatter.d.ts +71 -0
  156. package/dist/tools/compareTransactions/formatter.js +97 -0
  157. package/dist/tools/compareTransactions/index.d.ts +30 -0
  158. package/dist/tools/compareTransactions/index.js +160 -0
  159. package/dist/tools/compareTransactions/matcher.d.ts +12 -0
  160. package/dist/tools/compareTransactions/matcher.js +140 -0
  161. package/dist/tools/compareTransactions/parser.d.ts +14 -0
  162. package/dist/tools/compareTransactions/parser.js +430 -0
  163. package/dist/tools/compareTransactions/types.d.ts +27 -0
  164. package/dist/tools/compareTransactions/types.js +1 -0
  165. package/dist/tools/compareTransactions.d.ts +1 -0
  166. package/dist/tools/compareTransactions.js +1 -0
  167. package/dist/tools/deltaFetcher.d.ts +22 -0
  168. package/dist/tools/deltaFetcher.js +137 -0
  169. package/dist/tools/deltaSupport.d.ts +20 -0
  170. package/dist/tools/deltaSupport.js +176 -0
  171. package/dist/tools/exportTransactions.d.ts +17 -0
  172. package/dist/tools/exportTransactions.js +191 -0
  173. package/dist/tools/monthTools.d.ts +16 -0
  174. package/dist/tools/monthTools.js +107 -0
  175. package/dist/tools/payeeTools.d.ts +17 -0
  176. package/dist/tools/payeeTools.js +82 -0
  177. package/dist/tools/reconcileAdapter.d.ts +25 -0
  178. package/dist/tools/reconcileAdapter.js +167 -0
  179. package/dist/tools/reconciliation/analyzer.d.ts +3 -0
  180. package/dist/tools/reconciliation/analyzer.js +567 -0
  181. package/dist/tools/reconciliation/executor.d.ts +94 -0
  182. package/dist/tools/reconciliation/executor.js +611 -0
  183. package/dist/tools/reconciliation/index.d.ts +54 -0
  184. package/dist/tools/reconciliation/index.js +249 -0
  185. package/dist/tools/reconciliation/matcher.d.ts +3 -0
  186. package/dist/tools/reconciliation/matcher.js +160 -0
  187. package/dist/tools/reconciliation/payeeNormalizer.d.ts +6 -0
  188. package/dist/tools/reconciliation/payeeNormalizer.js +77 -0
  189. package/dist/tools/reconciliation/recommendationEngine.d.ts +2 -0
  190. package/dist/tools/reconciliation/recommendationEngine.js +273 -0
  191. package/dist/tools/reconciliation/reportFormatter.d.ts +13 -0
  192. package/dist/tools/reconciliation/reportFormatter.js +214 -0
  193. package/dist/tools/reconciliation/types.d.ts +172 -0
  194. package/dist/tools/reconciliation/types.js +7 -0
  195. package/dist/tools/schemas/outputs/accountOutputs.d.ts +58 -0
  196. package/dist/tools/schemas/outputs/accountOutputs.js +24 -0
  197. package/dist/tools/schemas/outputs/budgetOutputs.d.ts +48 -0
  198. package/dist/tools/schemas/outputs/budgetOutputs.js +15 -0
  199. package/dist/tools/schemas/outputs/categoryOutputs.d.ts +93 -0
  200. package/dist/tools/schemas/outputs/categoryOutputs.js +37 -0
  201. package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +269 -0
  202. package/dist/tools/schemas/outputs/comparisonOutputs.js +181 -0
  203. package/dist/tools/schemas/outputs/index.d.ts +14 -0
  204. package/dist/tools/schemas/outputs/index.js +14 -0
  205. package/dist/tools/schemas/outputs/monthOutputs.d.ts +122 -0
  206. package/dist/tools/schemas/outputs/monthOutputs.js +51 -0
  207. package/dist/tools/schemas/outputs/payeeOutputs.d.ts +34 -0
  208. package/dist/tools/schemas/outputs/payeeOutputs.js +16 -0
  209. package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +1275 -0
  210. package/dist/tools/schemas/outputs/reconciliationOutputs.js +377 -0
  211. package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +717 -0
  212. package/dist/tools/schemas/outputs/transactionMutationOutputs.js +260 -0
  213. package/dist/tools/schemas/outputs/transactionOutputs.d.ts +98 -0
  214. package/dist/tools/schemas/outputs/transactionOutputs.js +49 -0
  215. package/dist/tools/schemas/outputs/utilityOutputs.d.ts +219 -0
  216. package/dist/tools/schemas/outputs/utilityOutputs.js +120 -0
  217. package/dist/tools/schemas/shared/commonOutputs.d.ts +24 -0
  218. package/dist/tools/schemas/shared/commonOutputs.js +27 -0
  219. package/dist/tools/toolCategories.d.ts +32 -0
  220. package/dist/tools/toolCategories.js +32 -0
  221. package/dist/tools/transactionTools.d.ts +315 -0
  222. package/dist/tools/transactionTools.js +1722 -0
  223. package/dist/tools/utilityTools.d.ts +10 -0
  224. package/dist/tools/utilityTools.js +56 -0
  225. package/dist/types/index.d.ts +20 -0
  226. package/dist/types/index.js +16 -0
  227. package/dist/types/toolAnnotations.d.ts +7 -0
  228. package/dist/types/toolAnnotations.js +1 -0
  229. package/dist/utils/amountUtils.d.ts +3 -0
  230. package/dist/utils/amountUtils.js +10 -0
  231. package/dist/utils/dateUtils.d.ts +9 -0
  232. package/dist/utils/dateUtils.js +43 -0
  233. package/dist/utils/money.d.ts +21 -0
  234. package/dist/utils/money.js +51 -0
  235. package/docs/README.md +72 -0
  236. package/docs/assets/examples/reconciliation-with-recommendations.json +68 -0
  237. package/docs/assets/schemas/reconciliation-v2.json +338 -0
  238. package/docs/getting-started/CONFIGURATION.md +175 -0
  239. package/docs/getting-started/INSTALLATION.md +333 -0
  240. package/docs/getting-started/QUICKSTART.md +282 -0
  241. package/docs/guides/ARCHITECTURE.md +650 -0
  242. package/docs/guides/DEPLOYMENT.md +189 -0
  243. package/docs/guides/INTEGRATION_TESTING.md +730 -0
  244. package/docs/guides/TESTING.md +591 -0
  245. package/docs/reconciliation-flow.md +83 -0
  246. package/docs/reference/API.md +1450 -0
  247. package/docs/reference/EXAMPLES.md +946 -0
  248. package/docs/reference/TOOLS.md +348 -0
  249. package/docs/reference/TROUBLESHOOTING.md +481 -0
  250. package/esbuild.config.mjs +68 -0
  251. package/eslint.config.js +49 -0
  252. package/fix-types.sh +17 -0
  253. package/meta.json +12550 -0
  254. package/package.json +105 -0
  255. package/package.json.tmp +105 -0
  256. package/scripts/analyze-bundle.mjs +41 -0
  257. package/scripts/create-pr-description.js +203 -0
  258. package/scripts/generate-mcpb.ps1 +96 -0
  259. package/scripts/run-domain-integration-tests.js +33 -0
  260. package/scripts/run-generate-mcpb.js +29 -0
  261. package/scripts/run-throttled-integration-tests.js +116 -0
  262. package/scripts/test-delta-params.mjs +140 -0
  263. package/scripts/test-recommendations.ts +53 -0
  264. package/scripts/tmpTransaction.ts +48 -0
  265. package/scripts/validate-env.js +122 -0
  266. package/scripts/verify-build.js +105 -0
  267. package/scripts/watch-and-restart.ps1 +50 -0
  268. package/src/__tests__/comprehensive.integration.test.ts +1196 -0
  269. package/src/__tests__/delta.performance.test.ts +80 -0
  270. package/src/__tests__/performance.test.ts +725 -0
  271. package/src/__tests__/setup.ts +449 -0
  272. package/src/__tests__/testRunner.ts +444 -0
  273. package/src/__tests__/testUtils.ts +563 -0
  274. package/src/__tests__/workflows.e2e.test.ts +1675 -0
  275. package/src/index.ts +124 -0
  276. package/src/server/.gitkeep +1 -0
  277. package/src/server/YNABMCPServer.ts +1188 -0
  278. package/src/server/__tests__/YNABMCPServer.integration.test.ts +903 -0
  279. package/src/server/__tests__/YNABMCPServer.test.ts +894 -0
  280. package/src/server/__tests__/budgetResolver.test.ts +425 -0
  281. package/src/server/__tests__/cacheManager.test.ts +880 -0
  282. package/src/server/__tests__/config.test.ts +166 -0
  283. package/src/server/__tests__/deltaCache.merge.test.ts +724 -0
  284. package/src/server/__tests__/deltaCache.swr.test.ts +168 -0
  285. package/src/server/__tests__/deltaCache.test.ts +774 -0
  286. package/src/server/__tests__/diagnostics.test.ts +823 -0
  287. package/src/server/__tests__/errorHandler.integration.test.ts +466 -0
  288. package/src/server/__tests__/errorHandler.test.ts +416 -0
  289. package/src/server/__tests__/prompts.test.ts +354 -0
  290. package/src/server/__tests__/rateLimiter.test.ts +314 -0
  291. package/src/server/__tests__/requestLogger.test.ts +408 -0
  292. package/src/server/__tests__/resources.test.ts +299 -0
  293. package/src/server/__tests__/security.integration.test.ts +426 -0
  294. package/src/server/__tests__/securityMiddleware.test.ts +449 -0
  295. package/src/server/__tests__/server-startup.integration.test.ts +477 -0
  296. package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -0
  297. package/src/server/__tests__/toolRegistry.test.ts +855 -0
  298. package/src/server/budgetResolver.ts +235 -0
  299. package/src/server/cacheManager.ts +503 -0
  300. package/src/server/config.ts +41 -0
  301. package/src/server/deltaCache.merge.ts +149 -0
  302. package/src/server/deltaCache.ts +341 -0
  303. package/src/server/diagnostics.ts +338 -0
  304. package/src/server/errorHandler.ts +756 -0
  305. package/src/server/prompts.ts +291 -0
  306. package/src/server/rateLimiter.ts +156 -0
  307. package/src/server/requestLogger.ts +344 -0
  308. package/src/server/resources.ts +168 -0
  309. package/src/server/responseFormatter.ts +51 -0
  310. package/src/server/securityMiddleware.ts +236 -0
  311. package/src/server/serverKnowledgeStore.ts +91 -0
  312. package/src/server/toolRegistry.ts +489 -0
  313. package/src/tools/.gitkeep +1 -0
  314. package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -0
  315. package/src/tools/__tests__/accountTools.integration.test.ts +117 -0
  316. package/src/tools/__tests__/accountTools.test.ts +653 -0
  317. package/src/tools/__tests__/budgetTools.delta.integration.test.ts +90 -0
  318. package/src/tools/__tests__/budgetTools.integration.test.ts +134 -0
  319. package/src/tools/__tests__/budgetTools.test.ts +423 -0
  320. package/src/tools/__tests__/categoryTools.delta.integration.test.ts +80 -0
  321. package/src/tools/__tests__/categoryTools.integration.test.ts +295 -0
  322. package/src/tools/__tests__/categoryTools.test.ts +622 -0
  323. package/src/tools/__tests__/compareTransactions/formatter.test.ts +486 -0
  324. package/src/tools/__tests__/compareTransactions/index.test.ts +383 -0
  325. package/src/tools/__tests__/compareTransactions/matcher.test.ts +410 -0
  326. package/src/tools/__tests__/compareTransactions/parser.test.ts +764 -0
  327. package/src/tools/__tests__/compareTransactions.test.ts +342 -0
  328. package/src/tools/__tests__/compareTransactions.window.test.ts +147 -0
  329. package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +76 -0
  330. package/src/tools/__tests__/deltaFetcher.test.ts +270 -0
  331. package/src/tools/__tests__/deltaSupport.test.ts +188 -0
  332. package/src/tools/__tests__/deltaTestUtils.ts +46 -0
  333. package/src/tools/__tests__/exportTransactions.test.ts +213 -0
  334. package/src/tools/__tests__/monthTools.delta.integration.test.ts +80 -0
  335. package/src/tools/__tests__/monthTools.integration.test.ts +174 -0
  336. package/src/tools/__tests__/monthTools.test.ts +523 -0
  337. package/src/tools/__tests__/payeeTools.delta.integration.test.ts +80 -0
  338. package/src/tools/__tests__/payeeTools.integration.test.ts +150 -0
  339. package/src/tools/__tests__/payeeTools.test.ts +445 -0
  340. package/src/tools/__tests__/transactionTools.integration.test.ts +762 -0
  341. package/src/tools/__tests__/transactionTools.test.ts +3521 -0
  342. package/src/tools/__tests__/utilityTools.integration.test.ts +128 -0
  343. package/src/tools/__tests__/utilityTools.test.ts +205 -0
  344. package/src/tools/accountTools.ts +283 -0
  345. package/src/tools/budgetTools.ts +112 -0
  346. package/src/tools/categoryTools.ts +366 -0
  347. package/src/tools/compareTransactions/formatter.ts +163 -0
  348. package/src/tools/compareTransactions/index.ts +228 -0
  349. package/src/tools/compareTransactions/matcher.ts +240 -0
  350. package/src/tools/compareTransactions/parser.ts +557 -0
  351. package/src/tools/compareTransactions/types.ts +60 -0
  352. package/src/tools/compareTransactions.ts +3 -0
  353. package/src/tools/deltaFetcher.ts +278 -0
  354. package/src/tools/deltaSupport.ts +293 -0
  355. package/src/tools/exportTransactions.ts +273 -0
  356. package/src/tools/monthTools.ts +164 -0
  357. package/src/tools/payeeTools.ts +140 -0
  358. package/src/tools/reconcileAdapter.ts +312 -0
  359. package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +122 -0
  360. package/src/tools/reconciliation/__tests__/adapter.test.ts +234 -0
  361. package/src/tools/reconciliation/__tests__/analyzer.test.ts +406 -0
  362. package/src/tools/reconciliation/__tests__/executor.integration.test.ts +366 -0
  363. package/src/tools/reconciliation/__tests__/executor.test.ts +779 -0
  364. package/src/tools/reconciliation/__tests__/matcher.test.ts +650 -0
  365. package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +278 -0
  366. package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +658 -0
  367. package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1000 -0
  368. package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +151 -0
  369. package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +573 -0
  370. package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +78 -0
  371. package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +47 -0
  372. package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +61 -0
  373. package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +49 -0
  374. package/src/tools/reconciliation/analyzer.ts +824 -0
  375. package/src/tools/reconciliation/executor.ts +880 -0
  376. package/src/tools/reconciliation/index.ts +400 -0
  377. package/src/tools/reconciliation/matcher.ts +269 -0
  378. package/src/tools/reconciliation/payeeNormalizer.ts +167 -0
  379. package/src/tools/reconciliation/recommendationEngine.ts +506 -0
  380. package/src/tools/reconciliation/reportFormatter.ts +363 -0
  381. package/src/tools/reconciliation/types.ts +314 -0
  382. package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +424 -0
  383. package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +310 -0
  384. package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +448 -0
  385. package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +519 -0
  386. package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +155 -0
  387. package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +288 -0
  388. package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +478 -0
  389. package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +370 -0
  390. package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +401 -0
  391. package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +213 -0
  392. package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +474 -0
  393. package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +333 -0
  394. package/src/tools/schemas/outputs/accountOutputs.ts +137 -0
  395. package/src/tools/schemas/outputs/budgetOutputs.ts +86 -0
  396. package/src/tools/schemas/outputs/categoryOutputs.ts +194 -0
  397. package/src/tools/schemas/outputs/comparisonOutputs.ts +600 -0
  398. package/src/tools/schemas/outputs/index.ts +270 -0
  399. package/src/tools/schemas/outputs/monthOutputs.ts +243 -0
  400. package/src/tools/schemas/outputs/payeeOutputs.ts +105 -0
  401. package/src/tools/schemas/outputs/reconciliationOutputs.ts +796 -0
  402. package/src/tools/schemas/outputs/transactionMutationOutputs.ts +758 -0
  403. package/src/tools/schemas/outputs/transactionOutputs.ts +243 -0
  404. package/src/tools/schemas/outputs/utilityOutputs.ts +411 -0
  405. package/src/tools/schemas/shared/commonOutputs.ts +140 -0
  406. package/src/tools/toolCategories.ts +140 -0
  407. package/src/tools/transactionTools.ts +2509 -0
  408. package/src/tools/utilityTools.ts +90 -0
  409. package/src/types/.gitkeep +1 -0
  410. package/src/types/__tests__/index.test.ts +52 -0
  411. package/src/types/index.ts +67 -0
  412. package/src/types/integration-tests.d.ts +35 -0
  413. package/src/types/toolAnnotations.ts +44 -0
  414. package/src/utils/__tests__/dateUtils.test.ts +170 -0
  415. package/src/utils/__tests__/money.test.ts +189 -0
  416. package/src/utils/amountUtils.ts +32 -0
  417. package/src/utils/dateUtils.ts +108 -0
  418. package/src/utils/money.ts +123 -0
  419. package/test-csv-sample.csv +28 -0
  420. package/test-exports/sample_bank_statement.csv +7 -0
  421. package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_09-04-53.json +23 -0
  422. package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_10-37-42.json +23 -0
  423. package/test-exports/ynab_account_e9ddc2a6_minimal_4items_2025-11-19_09-02-09.json +44 -0
  424. package/test-exports/ynab_account_e9ddc2a6_minimal_6items_2025-11-19_10-37-52.json +58 -0
  425. package/test-exports/ynab_since_2025-11-01_account_4c18e9f0_minimal_14items_2025-11-16_10-07-10.json +115 -0
  426. package/test-reconcile-autodetect.js +40 -0
  427. package/test-reconcile-tool.js +152 -0
  428. package/test-reconcile-with-csv.cjs +89 -0
  429. package/test-statement.csv +8 -0
  430. package/test_debug.js +47 -0
  431. package/test_simple.mjs +16 -0
  432. package/tsconfig.json +31 -0
  433. package/tsconfig.prod.json +18 -0
  434. package/vitest-reporters/split-json-reporter.ts +211 -0
  435. package/vitest.config.ts +96 -0
@@ -0,0 +1,278 @@
1
+ import * as ynab from 'ynab';
2
+ import { DeltaCache, type DeltaFetchResult } from '../server/deltaCache.js';
3
+ import {
4
+ mergeFlatEntities,
5
+ mergeCategories,
6
+ mergeTransactions,
7
+ mergeMonths,
8
+ } from '../server/deltaCache.merge.js';
9
+ import { CacheManager, CACHE_TTLS } from '../server/cacheManager.js';
10
+
11
+ export interface DeltaFetchOptions {
12
+ forceFullRefresh?: boolean;
13
+ ttl?: number;
14
+ }
15
+
16
+ export class DeltaFetcher {
17
+ constructor(
18
+ private readonly ynabAPI: ynab.API,
19
+ private readonly deltaCache: DeltaCache,
20
+ ) {}
21
+
22
+ async fetchAccounts(
23
+ budgetId: string,
24
+ options?: DeltaFetchOptions,
25
+ ): Promise<DeltaFetchResult<ynab.Account>> {
26
+ const cacheKey = CacheManager.generateKey('accounts', 'list', budgetId);
27
+ return await this.deltaCache.fetchWithDelta<ynab.Account>(
28
+ cacheKey,
29
+ budgetId,
30
+ async (lastKnowledge?: number) => {
31
+ const response =
32
+ lastKnowledge !== undefined
33
+ ? await this.ynabAPI.accounts.getAccounts(budgetId, lastKnowledge)
34
+ : await this.ynabAPI.accounts.getAccounts(budgetId);
35
+ return {
36
+ data: response.data.accounts,
37
+ serverKnowledge: response.data.server_knowledge ?? 0,
38
+ };
39
+ },
40
+ mergeFlatEntities,
41
+ this.buildDeltaOptions(CACHE_TTLS.ACCOUNTS, options),
42
+ );
43
+ }
44
+
45
+ async fetchCategories(
46
+ budgetId: string,
47
+ options?: DeltaFetchOptions,
48
+ ): Promise<DeltaFetchResult<ynab.CategoryGroupWithCategories>> {
49
+ const cacheKey = CacheManager.generateKey('categories', 'list', budgetId);
50
+ return await this.deltaCache.fetchWithDelta<ynab.CategoryGroupWithCategories>(
51
+ cacheKey,
52
+ budgetId,
53
+ async (lastKnowledge?: number) => {
54
+ const response =
55
+ lastKnowledge !== undefined
56
+ ? await this.ynabAPI.categories.getCategories(budgetId, lastKnowledge)
57
+ : await this.ynabAPI.categories.getCategories(budgetId);
58
+ return {
59
+ data: response.data.category_groups,
60
+ serverKnowledge: response.data.server_knowledge ?? 0,
61
+ };
62
+ },
63
+ mergeCategories,
64
+ this.buildDeltaOptions(CACHE_TTLS.CATEGORIES, options),
65
+ );
66
+ }
67
+
68
+ async fetchTransactions(
69
+ budgetId: string,
70
+ sinceDate?: string,
71
+ type?: ynab.GetTransactionsTypeEnum,
72
+ options?: DeltaFetchOptions,
73
+ ): Promise<DeltaFetchResult<ynab.TransactionDetail>> {
74
+ const normalizedSince = sinceDate ?? 'all';
75
+ const normalizedType = type ?? 'all';
76
+ const cacheKey = CacheManager.generateKey(
77
+ 'transactions',
78
+ 'list',
79
+ budgetId,
80
+ normalizedSince,
81
+ normalizedType,
82
+ );
83
+ return await this.deltaCache.fetchWithDelta<ynab.TransactionDetail>(
84
+ cacheKey,
85
+ budgetId,
86
+ async (lastKnowledge?: number) => {
87
+ const response = await this.ynabAPI.transactions.getTransactions(
88
+ budgetId,
89
+ sinceDate,
90
+ type,
91
+ lastKnowledge,
92
+ );
93
+ return {
94
+ data: response.data.transactions,
95
+ serverKnowledge: response.data.server_knowledge ?? 0,
96
+ };
97
+ },
98
+ mergeTransactions,
99
+ this.buildDeltaOptions(CACHE_TTLS.TRANSACTIONS, options),
100
+ );
101
+ }
102
+
103
+ async fetchTransactionsByAccount(
104
+ budgetId: string,
105
+ accountId: string,
106
+ sinceDate?: string,
107
+ options?: DeltaFetchOptions,
108
+ ): Promise<DeltaFetchResult<ynab.TransactionDetail>> {
109
+ const normalizedSince = sinceDate ?? 'all';
110
+ const cacheKey = CacheManager.generateKey(
111
+ 'transactions',
112
+ 'account',
113
+ budgetId,
114
+ accountId,
115
+ normalizedSince,
116
+ );
117
+ return await this.deltaCache.fetchWithDelta<ynab.TransactionDetail>(
118
+ cacheKey,
119
+ budgetId,
120
+ async (lastKnowledge?: number) => {
121
+ const response = await this.ynabAPI.transactions.getTransactionsByAccount(
122
+ budgetId,
123
+ accountId,
124
+ sinceDate,
125
+ undefined,
126
+ lastKnowledge,
127
+ );
128
+ return {
129
+ data: response.data.transactions,
130
+ serverKnowledge: response.data.server_knowledge ?? 0,
131
+ };
132
+ },
133
+ mergeTransactions,
134
+ this.buildDeltaOptions(CACHE_TTLS.TRANSACTIONS, options),
135
+ );
136
+ }
137
+
138
+ async fetchAccountsFull(budgetId: string): Promise<DeltaFetchResult<ynab.Account>> {
139
+ const response = await this.ynabAPI.accounts.getAccounts(budgetId);
140
+ const accounts = response.data.accounts.filter((account) => !account.deleted);
141
+ return {
142
+ data: accounts,
143
+ wasCached: false,
144
+ usedDelta: false,
145
+ serverKnowledge: response.data.server_knowledge ?? 0,
146
+ };
147
+ }
148
+
149
+ async fetchTransactionsByAccountFull(
150
+ budgetId: string,
151
+ accountId: string,
152
+ sinceDate?: string,
153
+ ): Promise<DeltaFetchResult<ynab.TransactionDetail>> {
154
+ const response = await this.ynabAPI.transactions.getTransactionsByAccount(
155
+ budgetId,
156
+ accountId,
157
+ sinceDate,
158
+ );
159
+ const transactions = response.data.transactions.filter((transaction) => !transaction.deleted);
160
+ return {
161
+ data: transactions,
162
+ wasCached: false,
163
+ usedDelta: false,
164
+ serverKnowledge: response.data.server_knowledge ?? 0,
165
+ };
166
+ }
167
+
168
+ async fetchScheduledTransactions(
169
+ budgetId: string,
170
+ options?: DeltaFetchOptions,
171
+ ): Promise<DeltaFetchResult<ynab.ScheduledTransactionDetail>> {
172
+ const cacheKey = CacheManager.generateKey('scheduled_transactions', 'list', budgetId);
173
+ return await this.deltaCache.fetchWithDelta<ynab.ScheduledTransactionDetail>(
174
+ cacheKey,
175
+ budgetId,
176
+ async (lastKnowledge?: number) => {
177
+ const response =
178
+ lastKnowledge !== undefined
179
+ ? await this.ynabAPI.scheduledTransactions.getScheduledTransactions(
180
+ budgetId,
181
+ lastKnowledge,
182
+ )
183
+ : await this.ynabAPI.scheduledTransactions.getScheduledTransactions(budgetId);
184
+ return {
185
+ data: response.data.scheduled_transactions,
186
+ serverKnowledge: response.data.server_knowledge ?? 0,
187
+ };
188
+ },
189
+ mergeFlatEntities,
190
+ this.buildDeltaOptions(CACHE_TTLS.SCHEDULED_TRANSACTIONS, options),
191
+ );
192
+ }
193
+
194
+ async fetchPayees(
195
+ budgetId: string,
196
+ options?: DeltaFetchOptions,
197
+ ): Promise<DeltaFetchResult<ynab.Payee>> {
198
+ const cacheKey = CacheManager.generateKey('payees', 'list', budgetId);
199
+ return await this.deltaCache.fetchWithDelta<ynab.Payee>(
200
+ cacheKey,
201
+ budgetId,
202
+ async (lastKnowledge?: number) => {
203
+ const response =
204
+ lastKnowledge !== undefined
205
+ ? await this.ynabAPI.payees.getPayees(budgetId, lastKnowledge)
206
+ : await this.ynabAPI.payees.getPayees(budgetId);
207
+ return {
208
+ data: response.data.payees,
209
+ serverKnowledge: response.data.server_knowledge ?? 0,
210
+ };
211
+ },
212
+ mergeFlatEntities,
213
+ this.buildDeltaOptions(CACHE_TTLS.PAYEES, options),
214
+ );
215
+ }
216
+
217
+ async fetchMonths(
218
+ budgetId: string,
219
+ options?: DeltaFetchOptions,
220
+ ): Promise<DeltaFetchResult<ynab.MonthSummary>> {
221
+ const cacheKey = CacheManager.generateKey('months', 'list', budgetId);
222
+ return await this.deltaCache.fetchWithDelta<ynab.MonthSummary>(
223
+ cacheKey,
224
+ budgetId,
225
+ async (lastKnowledge?: number) => {
226
+ const response =
227
+ lastKnowledge !== undefined
228
+ ? await this.ynabAPI.months.getBudgetMonths(budgetId, lastKnowledge)
229
+ : await this.ynabAPI.months.getBudgetMonths(budgetId);
230
+ return {
231
+ data: response.data.months,
232
+ serverKnowledge: response.data.server_knowledge ?? 0,
233
+ };
234
+ },
235
+ mergeMonths,
236
+ this.buildDeltaOptions(CACHE_TTLS.MONTHS, options),
237
+ );
238
+ }
239
+
240
+ async fetchBudgets(options?: DeltaFetchOptions): Promise<DeltaFetchResult<ynab.BudgetSummary>> {
241
+ const cacheKey = CacheManager.generateKey('budgets', 'list');
242
+ const result = await this.deltaCache.fetchWithDelta<ynab.BudgetSummary & { deleted?: boolean }>(
243
+ cacheKey,
244
+ 'global',
245
+ async () => {
246
+ const response = await this.ynabAPI.budgets.getBudgets();
247
+ const serverKnowledge =
248
+ (response.data as { server_knowledge?: number }).server_knowledge ?? 0;
249
+ return {
250
+ data: response.data.budgets,
251
+ serverKnowledge,
252
+ };
253
+ },
254
+ mergeFlatEntities,
255
+ {
256
+ ttl: options?.ttl ?? CACHE_TTLS.BUDGETS,
257
+ // TODO: Support delta responses when merge logic can handle nested objects safely.
258
+ forceFullRefresh: true,
259
+ },
260
+ );
261
+ return {
262
+ ...result,
263
+ data: result.data as ynab.BudgetSummary[],
264
+ };
265
+ }
266
+
267
+ private buildDeltaOptions(
268
+ defaultTtl: number,
269
+ options?: DeltaFetchOptions,
270
+ ): { ttl: number; forceFullRefresh?: boolean } {
271
+ return {
272
+ ttl: options?.ttl ?? defaultTtl,
273
+ ...(options?.forceFullRefresh !== undefined && {
274
+ forceFullRefresh: options.forceFullRefresh,
275
+ }),
276
+ };
277
+ }
278
+ }
@@ -0,0 +1,293 @@
1
+ import * as ynab from 'ynab';
2
+ import { cacheManager } from '../server/cacheManager.js';
3
+ import { DeltaCache } from '../server/deltaCache.js';
4
+ import { ServerKnowledgeStore } from '../server/serverKnowledgeStore.js';
5
+ import { DeltaFetcher } from './deltaFetcher.js';
6
+
7
+ interface SharedDeltaSupportContext {
8
+ deltaFetcher?: DeltaFetcher;
9
+ deltaCache?: DeltaCache;
10
+ knowledgeStore?: ServerKnowledgeStore;
11
+ ynabAPI?: ynab.API;
12
+ manuallyConfigured?: boolean;
13
+ }
14
+
15
+ export interface SharedDeltaSupportOptions {
16
+ deltaFetcher?: DeltaFetcher;
17
+ deltaCache?: DeltaCache;
18
+ knowledgeStore?: ServerKnowledgeStore;
19
+ }
20
+
21
+ let sharedDeltaContext: SharedDeltaSupportContext | undefined;
22
+
23
+ export function setSharedDeltaSupport(options?: SharedDeltaSupportOptions): void {
24
+ if (!options) {
25
+ sharedDeltaContext = undefined;
26
+ return;
27
+ }
28
+ sharedDeltaContext = {
29
+ ...options,
30
+ manuallyConfigured: true,
31
+ };
32
+ }
33
+
34
+ export function getSharedDeltaSupport(): SharedDeltaSupportOptions | undefined {
35
+ if (!sharedDeltaContext) {
36
+ return undefined;
37
+ }
38
+ const result: SharedDeltaSupportOptions = {};
39
+ if (sharedDeltaContext.deltaFetcher) {
40
+ result.deltaFetcher = sharedDeltaContext.deltaFetcher;
41
+ }
42
+ if (sharedDeltaContext.deltaCache) {
43
+ result.deltaCache = sharedDeltaContext.deltaCache;
44
+ }
45
+ if (sharedDeltaContext.knowledgeStore) {
46
+ result.knowledgeStore = sharedDeltaContext.knowledgeStore;
47
+ }
48
+ return result;
49
+ }
50
+
51
+ function resolveSharedDeltaFetcher(ynabAPI: ynab.API): DeltaFetcher {
52
+ if (
53
+ sharedDeltaContext &&
54
+ !sharedDeltaContext.manuallyConfigured &&
55
+ sharedDeltaContext.ynabAPI &&
56
+ sharedDeltaContext.ynabAPI !== ynabAPI
57
+ ) {
58
+ sharedDeltaContext = undefined;
59
+ }
60
+
61
+ if (sharedDeltaContext?.deltaFetcher) {
62
+ return sharedDeltaContext.deltaFetcher;
63
+ }
64
+
65
+ const context: SharedDeltaSupportContext = sharedDeltaContext ? { ...sharedDeltaContext } : {};
66
+ if (context.manuallyConfigured === undefined) {
67
+ context.manuallyConfigured = false;
68
+ }
69
+
70
+ if (!context.deltaCache) {
71
+ if (!context.knowledgeStore) {
72
+ context.knowledgeStore = new ServerKnowledgeStore();
73
+ }
74
+ context.deltaCache = new DeltaCache(cacheManager, context.knowledgeStore);
75
+ }
76
+
77
+ const deltaFetcher = new DeltaFetcher(ynabAPI, context.deltaCache);
78
+ context.deltaFetcher = deltaFetcher;
79
+ if (!context.ynabAPI) {
80
+ context.ynabAPI = ynabAPI;
81
+ }
82
+
83
+ sharedDeltaContext = context;
84
+
85
+ return deltaFetcher;
86
+ }
87
+
88
+ /**
89
+ * Type guard to check if a value is a DeltaFetcher instance.
90
+ */
91
+ function isDeltaFetcher(value: unknown): value is DeltaFetcher {
92
+ return (
93
+ value !== null &&
94
+ typeof value === 'object' &&
95
+ value instanceof DeltaFetcher &&
96
+ typeof (value as DeltaFetcher).fetchAccounts === 'function' &&
97
+ typeof (value as DeltaFetcher).fetchCategories === 'function'
98
+ );
99
+ }
100
+
101
+ /**
102
+ * Type guard to check if a value is a DeltaCache instance.
103
+ */
104
+ function isDeltaCache(value: unknown): value is DeltaCache {
105
+ return (
106
+ value !== null &&
107
+ typeof value === 'object' &&
108
+ value instanceof DeltaCache &&
109
+ typeof (value as DeltaCache).fetchWithDelta === 'function'
110
+ );
111
+ }
112
+
113
+ /**
114
+ * Type guard to check if a value is a ServerKnowledgeStore instance.
115
+ */
116
+ function isServerKnowledgeStore(value: unknown): value is ServerKnowledgeStore {
117
+ return (
118
+ value !== null &&
119
+ typeof value === 'object' &&
120
+ value instanceof ServerKnowledgeStore &&
121
+ typeof (value as ServerKnowledgeStore).get === 'function' &&
122
+ typeof (value as ServerKnowledgeStore).update === 'function'
123
+ );
124
+ }
125
+
126
+ /**
127
+ * Type guard to check if a value is a plain object (params).
128
+ */
129
+ function isParamsObject(value: unknown): value is Record<string, unknown> {
130
+ return (
131
+ value !== null &&
132
+ typeof value === 'object' &&
133
+ !Array.isArray(value) &&
134
+ // Not a class instance
135
+ (value.constructor === Object || value.constructor === undefined)
136
+ );
137
+ }
138
+
139
+ /**
140
+ * Helper to get a descriptive type name for error messages.
141
+ */
142
+ function getTypeName(value: unknown): string {
143
+ if (value === null) return 'null';
144
+ if (value === undefined) return 'undefined';
145
+ const type = typeof value;
146
+ if (type !== 'object') return type;
147
+ const constructorName = (value as Record<string, unknown>).constructor?.name;
148
+ return constructorName ? `${type} (${constructorName})` : type;
149
+ }
150
+
151
+ export function resolveDeltaFetcherArgs<TParams extends Record<string, unknown>>(
152
+ ynabAPI: ynab.API,
153
+ deltaFetcherOrParams: DeltaFetcher | TParams,
154
+ maybeParams?: TParams,
155
+ ): { deltaFetcher: DeltaFetcher; params: TParams } {
156
+ // Case 1: Three arguments - (ynabAPI, deltaFetcher, params)
157
+ if (maybeParams !== undefined) {
158
+ // Validate that deltaFetcherOrParams is actually a DeltaFetcher
159
+ if (!isDeltaFetcher(deltaFetcherOrParams)) {
160
+ throw new Error(
161
+ 'resolveDeltaFetcherArgs: When providing 3 arguments, the second argument must be a DeltaFetcher instance. ' +
162
+ `Got: ${getTypeName(deltaFetcherOrParams)}`,
163
+ );
164
+ }
165
+
166
+ // Validate that maybeParams is a params object
167
+ if (!isParamsObject(maybeParams)) {
168
+ throw new Error(
169
+ 'resolveDeltaFetcherArgs: When providing 3 arguments, the third argument must be a params object. ' +
170
+ `Got: ${getTypeName(maybeParams)}`,
171
+ );
172
+ }
173
+
174
+ return {
175
+ deltaFetcher: deltaFetcherOrParams,
176
+ params: maybeParams,
177
+ };
178
+ }
179
+
180
+ // Case 2: Two arguments - (ynabAPI, params)
181
+ // Validate that deltaFetcherOrParams is a params object, not a DeltaFetcher
182
+ if (isDeltaFetcher(deltaFetcherOrParams)) {
183
+ throw new Error(
184
+ 'resolveDeltaFetcherArgs: When providing 2 arguments, the second argument must be a params object, not a DeltaFetcher. ' +
185
+ 'To use a custom DeltaFetcher, provide all 3 arguments: (ynabAPI, deltaFetcher, params)',
186
+ );
187
+ }
188
+
189
+ if (!isParamsObject(deltaFetcherOrParams)) {
190
+ throw new Error(
191
+ 'resolveDeltaFetcherArgs: When providing 2 arguments, the second argument must be a params object. ' +
192
+ `Got: ${getTypeName(deltaFetcherOrParams)}`,
193
+ );
194
+ }
195
+
196
+ return {
197
+ deltaFetcher: resolveSharedDeltaFetcher(ynabAPI),
198
+ params: deltaFetcherOrParams,
199
+ };
200
+ }
201
+
202
+ export function resolveDeltaWriteArgs<TParams extends Record<string, unknown>>(
203
+ deltaCacheOrParams: DeltaCache | TParams,
204
+ knowledgeStoreOrParams?: ServerKnowledgeStore | TParams,
205
+ maybeParams?: TParams,
206
+ ): { deltaCache: DeltaCache; knowledgeStore: ServerKnowledgeStore; params: TParams } {
207
+ // Case 1: Three arguments - (deltaCache, knowledgeStore, params)
208
+ if (maybeParams !== undefined) {
209
+ // Validate that deltaCacheOrParams is actually a DeltaCache
210
+ if (!isDeltaCache(deltaCacheOrParams)) {
211
+ throw new Error(
212
+ 'resolveDeltaWriteArgs: When providing 3 arguments, the first argument must be a DeltaCache instance. ' +
213
+ `Got: ${getTypeName(deltaCacheOrParams)}`,
214
+ );
215
+ }
216
+
217
+ // Validate that knowledgeStoreOrParams is actually a ServerKnowledgeStore
218
+ if (!isServerKnowledgeStore(knowledgeStoreOrParams)) {
219
+ throw new Error(
220
+ 'resolveDeltaWriteArgs: When providing 3 arguments, the second argument must be a ServerKnowledgeStore instance. ' +
221
+ `Got: ${getTypeName(knowledgeStoreOrParams)}`,
222
+ );
223
+ }
224
+
225
+ // Validate that maybeParams is a params object
226
+ if (!isParamsObject(maybeParams)) {
227
+ throw new Error(
228
+ 'resolveDeltaWriteArgs: When providing 3 arguments, the third argument must be a params object. ' +
229
+ `Got: ${getTypeName(maybeParams)}`,
230
+ );
231
+ }
232
+
233
+ return {
234
+ deltaCache: deltaCacheOrParams,
235
+ knowledgeStore: knowledgeStoreOrParams,
236
+ params: maybeParams,
237
+ };
238
+ }
239
+
240
+ // Case 2: Two arguments - could be (deltaCache, params) or just (params)
241
+ // Need to determine if knowledgeStoreOrParams is a ServerKnowledgeStore or params
242
+ if (knowledgeStoreOrParams !== undefined) {
243
+ const isKnowledgeStore = isServerKnowledgeStore(knowledgeStoreOrParams);
244
+ const isParams = isParamsObject(knowledgeStoreOrParams);
245
+
246
+ if (!isKnowledgeStore && !isParams) {
247
+ throw new Error(
248
+ 'resolveDeltaWriteArgs: When providing 2 arguments, the second argument must be either a ServerKnowledgeStore or a params object. ' +
249
+ `Got: ${getTypeName(knowledgeStoreOrParams)}`,
250
+ );
251
+ }
252
+
253
+ // If second arg is a params object, then first arg should be params too (invalid)
254
+ if (isParams) {
255
+ throw new Error(
256
+ 'resolveDeltaWriteArgs: Invalid argument combination. When providing 2 arguments where the second is a params object, ' +
257
+ 'this is ambiguous. Either provide 1 argument (params only) or 3 arguments (deltaCache, knowledgeStore, params).',
258
+ );
259
+ }
260
+
261
+ // Second arg is ServerKnowledgeStore, so first arg must be DeltaCache (invalid - missing params)
262
+ if (isKnowledgeStore) {
263
+ throw new Error(
264
+ 'resolveDeltaWriteArgs: When providing DeltaCache and ServerKnowledgeStore, you must also provide params as the third argument. ' +
265
+ 'Got 2 arguments, expected 3: (deltaCache, knowledgeStore, params)',
266
+ );
267
+ }
268
+ }
269
+
270
+ // Case 3: One argument - (params)
271
+ // Validate that deltaCacheOrParams is a params object, not a DeltaCache
272
+ if (isDeltaCache(deltaCacheOrParams)) {
273
+ throw new Error(
274
+ 'resolveDeltaWriteArgs: When providing only 1 argument, it must be a params object, not a DeltaCache. ' +
275
+ 'To use a custom DeltaCache, provide all 3 arguments: (deltaCache, knowledgeStore, params)',
276
+ );
277
+ }
278
+
279
+ if (!isParamsObject(deltaCacheOrParams)) {
280
+ throw new Error(
281
+ 'resolveDeltaWriteArgs: When providing only 1 argument, it must be a params object. ' +
282
+ `Got: ${getTypeName(deltaCacheOrParams)}`,
283
+ );
284
+ }
285
+
286
+ const fallbackKnowledgeStore = new ServerKnowledgeStore();
287
+ const fallbackDeltaCache = new DeltaCache(cacheManager, fallbackKnowledgeStore);
288
+ return {
289
+ deltaCache: fallbackDeltaCache,
290
+ knowledgeStore: fallbackKnowledgeStore,
291
+ params: deltaCacheOrParams,
292
+ };
293
+ }