@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.
- package/.chunkhound.json +11 -0
- package/.code/agents/0427d95e-edca-431f-a214-5e53264e29c4/error.txt +8 -0
- package/.code/agents/0d675174-d1e1-41c3-9975-4c2e275819a9/error.txt +3 -0
- package/.code/agents/0d8c5afd-4787-422b-abf8-2e5943fc7e67/error.txt +3 -0
- package/.code/agents/0ec34a70-ed5d-4b9e-bee4-bb0e4cccbc4b/error.txt +1 -0
- package/.code/agents/0ef51a21-1ab1-49d7-9561-0eaa43875ebc/error.txt +12 -0
- package/.code/agents/15db95d7-abad-4b4d-9c3b-8446089cb61d/error.txt +1 -0
- package/.code/agents/19ab9acb-f675-4ff0-902a-09a5476f8149/error.txt +1 -0
- package/.code/agents/1ef7e12d-f6ff-4897-8a9b-152d523d898e/error.txt +5 -0
- package/.code/agents/2465/exec-call_lroN9KKzJVWC7t5423DK1nT9.txt +1453 -0
- package/.code/agents/28edb6fe-95a9-41a0-ae69-aa0100d26c0c/error.txt +8 -0
- package/.code/agents/2ae40cf5-b4bf-42e2-92bf-7ea350a7755e/error.txt +9 -0
- package/.code/agents/2bfc4e1f-ac4b-45a5-b6df-bf89d4dbb54c/error.txt +1 -0
- package/.code/agents/2e2e1134-eff0-49be-ba25-8e2c3468a564/error.txt +5 -0
- package/.code/agents/3/exec-call_203OC4TNVkLxW7z2HCVEQ1cM.txt +81 -0
- package/.code/agents/3/exec-call_SS5T0XSiXB4LSNzUKTl75wkh.txt +610 -0
- package/.code/agents/3322c003-ce5e-48e3-a342-f5049c5bf9a2/error.txt +1 -0
- package/.code/agents/391e9b08-1ebc-468c-9bcd-6d0cc3193b37/error.txt +1 -0
- package/.code/agents/3ab0aa84-b7bb-4054-afa3-40b8fd7d3be0/error.txt +1 -0
- package/.code/agents/3bed368d-50fe-477e-aee3-a6707eaa1ab9/error.txt +3 -0
- package/.code/agents/3e40b925-db12-442f-8d7a-a25fc69a6672/error.txt +8 -0
- package/.code/agents/414d5776-cf58-41f3-9328-a6daed503a50/error.txt +5 -0
- package/.code/agents/42687751-4565-4610-b240-67835b17d861/error.txt +1 -0
- package/.code/agents/46b98876-1a39-43c9-9e2f-507ca6d47335/error.txt +9 -0
- package/.code/agents/4a7d9491-b26f-43dd-850d-2ecdc49b5d1b/error.txt +1 -0
- package/.code/agents/4e60f00a-1b3e-447f-87f3-7faf9deddec3/error.txt +13 -0
- package/.code/agents/5138fc1c-4d49-4b74-a7da-ccdb3a8e44e7/error.txt +14 -0
- package/.code/agents/521cff39-a7a3-42e5-a557-134f0f7daaa0/error.txt +5 -0
- package/.code/agents/53302dc5-3857-4413-9a47-9e0f64a51dc4/error.txt +5 -0
- package/.code/agents/567c7c2e-6a6f-4761-a08d-d36deeb2e0ac/error.txt +5 -0
- package/.code/agents/57b00845-80dc-47c9-953c-3028d16275d6/error.txt +3 -0
- package/.code/agents/593d9005-c2a5-48fd-8813-ece0d3f2de96/error.txt +1 -0
- package/.code/agents/5a112e66-0e1a-42f9-877c-53af56ea3551/error.txt +1 -0
- package/.code/agents/5b05e8ed-7788-4738-b7ee-9faa8180f992/error.txt +5 -0
- package/.code/agents/5f888d6f-d7ca-4ac8-be23-9ea1bf753951/error.txt +5 -0
- package/.code/agents/607db3ab-e4b0-435b-b497-93e9aa525549/error.txt +8 -0
- package/.code/agents/67dcb2a2-900f-4c78-b3fc-80b5213e0ddf/error.txt +8 -0
- package/.code/agents/69ad848c-4e98-49b3-b16c-0094ac2d1759/error.txt +5 -0
- package/.code/agents/6c9cfc5f-0d0b-445c-b121-9f60082c4f70/error.txt +1 -0
- package/.code/agents/6f6f8f77-4ab0-4f6e-9f30-40e8be0bd8f5/error.txt +1 -0
- package/.code/agents/72a7cde4-fa8a-4024-9038-27faa550539b/error.txt +1 -0
- package/.code/agents/7b48335c-8247-43aa-9949-5f820ba8e199/error.txt +1 -0
- package/.code/agents/80944249-bea9-4ac5-87de-a666c4df306e/error.txt +1 -0
- package/.code/agents/826099df-1b66-4186-a915-7eb59f9db19d/error.txt +5 -0
- package/.code/agents/8291d158-18a8-4a92-b799-4e9a4d9cce88/error.txt +1 -0
- package/.code/agents/82fb71a3-20fb-4341-804a-a2fc900f95bc/error.txt +1 -0
- package/.code/agents/855790ea-54ee-43e4-8209-a66994e37590/error.txt +1 -0
- package/.code/agents/88ce3a2e-04f2-42be-9062-bf97aa798da0/error.txt +3 -0
- package/.code/agents/9a17e398-b6ed-4218-bb55-bc64a8d38ce8/error.txt +8 -0
- package/.code/agents/9a4f4bfc-a2a6-4f40-a896-9335b41a7ed1/error.txt +1 -0
- package/.code/agents/9b633e55-ef84-47d6-94bb-fd3dd172ad97/error.txt +1 -0
- package/.code/agents/9b81f3ab-c72b-4a81-9a8f-28a49ddba84a/error.txt +8 -0
- package/.code/agents/a35daf29-b2d1-4aef-9b42-dad63a76bd47/error.txt +3 -0
- package/.code/agents/a81990cc-69ee-44d2-b907-17403c9bc5d7/error.txt +5 -0
- package/.code/agents/ab56260a-4a83-4ad4-9410-f88a23d6520a/error.txt +1 -0
- package/.code/agents/ad722c31-2d1d-45f7-bae2-3f02ca455b60/error.txt +1 -0
- package/.code/agents/b62e8690-3324-4b97-9309-731bee79416b/error.txt +5 -0
- package/.code/agents/baf60a3a-752b-4ad8-99d6-df32423ed2eb/error.txt +1 -0
- package/.code/agents/be049042-7dcb-4ac8-9beb-c8f1aea67742/error.txt +14 -0
- package/.code/agents/bed1dcb4-bfce-4a9f-8594-0f994962aafd/error.txt +1 -0
- package/.code/agents/c324a6cf-e935-4ede-9529-b3ebc18e8d6b/error.txt +5 -0
- package/.code/agents/c37c06ff-dfe3-43f2-9bbc-3ec73ec8f41d/error.txt +5 -0
- package/.code/agents/c8cd6671-433a-456b-9f88-e51cb2df6bfc/error.txt +11 -0
- package/.code/agents/ca2ccb67-2f24-428e-b27d-9365beadd140/error.txt +1 -0
- package/.code/agents/cf08c0c8-e7f0-423e-93ba-547e8e818340/error.txt +8 -0
- package/.code/agents/d579c74f-874b-40a4-9d56-ced1eb6a701d/error.txt +1 -0
- package/.code/agents/df412c98-7378-4deb-8e1e-76c416931181/error.txt +3 -0
- package/.code/agents/e5134eb3-2af4-45b0-8998-051cb4afdb45/error.txt +3 -0
- package/.code/agents/e6308471-aa45-4e9e-9496-2e9404164d97/error.txt +8 -0
- package/.code/agents/e7bd8bc7-23fb-4f46-98dc-b0dcf11b75a1/error.txt +1 -0
- package/.code/agents/e92bec35-378d-4fe1-8ac0-6e1bb3c86911/error.txt +5 -0
- package/.code/agents/ed918fbf-2dc4-4aa2-bfc5-04b65d9471ea/error.txt +1 -0
- package/.code/agents/ef1d756f-b272-48fc-8729-f05c494674f7/error.txt +1 -0
- package/.code/agents/ef359853-0249-4e41-a804-c0fc459fe456/error.txt +1 -0
- package/.code/agents/effc7b4a-4b90-40a0-8c86-a7a99d2d5fd2/error.txt +1 -0
- package/.code/agents/fa15f8d5-8359-4a8b-83a3-2f2056b3ff40/error.txt +3 -0
- package/.code/agents/fbef4193-eadf-4c8a-83ff-4878a6310f25/error.txt +8 -0
- package/.code/agents/fd0a4b4a-fda4-4964-a6d6-2b8a2da387c6/error.txt +1 -0
- package/.dxtignore +57 -0
- package/.env.example +44 -0
- package/.gemini/settings.json +8 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +41 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
- package/.github/ISSUE_TEMPLATE/release_checklist.md +31 -0
- package/.github/pull_request_template.md +41 -0
- package/.github/workflows/ci-tests.yml +41 -0
- package/.github/workflows/claude-code-review.yml +57 -0
- package/.github/workflows/claude.yml +50 -0
- package/.github/workflows/full-integration.yml +22 -0
- package/.github/workflows/pr-description-check.yml +88 -0
- package/.github/workflows/publish.yml +33 -0
- package/.github/workflows/release.yml +89 -0
- package/.mcpbignore +58 -0
- package/.prettierignore +10 -0
- package/.prettierrc.json +10 -0
- package/ADOS-2-Module-1-Complete-Manual.md +757 -0
- package/AGENTS.md +36 -0
- package/CHANGELOG.md +187 -0
- package/CLAUDE.md +414 -0
- package/CODEREVIEW_RESPONSE.md +128 -0
- package/LICENSE +17 -0
- package/NUL +1 -0
- package/README.md +222 -0
- package/SCHEMA_IMPROVEMENT_SUMMARY.md +120 -0
- package/TESTING_NOTES.md +217 -0
- package/WARP.md +245 -0
- package/accountactivity-merged.csv +149 -0
- package/bin/ynab-mcp-server.cjs +4 -0
- package/bin/ynab-mcp-server.js +8 -0
- package/bundle-analysis.html +13110 -0
- package/dist/bundle/index.cjs +124 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +85 -0
- package/dist/server/YNABMCPServer.d.ts +264 -0
- package/dist/server/YNABMCPServer.js +845 -0
- package/dist/server/budgetResolver.d.ts +15 -0
- package/dist/server/budgetResolver.js +99 -0
- package/dist/server/cacheManager.d.ts +74 -0
- package/dist/server/cacheManager.js +306 -0
- package/dist/server/config.d.ts +3 -0
- package/dist/server/config.js +19 -0
- package/dist/server/deltaCache.d.ts +61 -0
- package/dist/server/deltaCache.js +206 -0
- package/dist/server/deltaCache.merge.d.ts +9 -0
- package/dist/server/deltaCache.merge.js +111 -0
- package/dist/server/diagnostics.d.ts +90 -0
- package/dist/server/diagnostics.js +163 -0
- package/dist/server/errorHandler.d.ts +69 -0
- package/dist/server/errorHandler.js +524 -0
- package/dist/server/prompts.d.ts +31 -0
- package/dist/server/prompts.js +205 -0
- package/dist/server/rateLimiter.d.ts +27 -0
- package/dist/server/rateLimiter.js +82 -0
- package/dist/server/requestLogger.d.ts +62 -0
- package/dist/server/requestLogger.js +190 -0
- package/dist/server/resources.d.ts +39 -0
- package/dist/server/resources.js +85 -0
- package/dist/server/responseFormatter.d.ts +14 -0
- package/dist/server/responseFormatter.js +42 -0
- package/dist/server/securityMiddleware.d.ts +87 -0
- package/dist/server/securityMiddleware.js +117 -0
- package/dist/server/serverKnowledgeStore.d.ts +11 -0
- package/dist/server/serverKnowledgeStore.js +42 -0
- package/dist/server/toolRegistry.d.ts +85 -0
- package/dist/server/toolRegistry.js +272 -0
- package/dist/tools/__tests__/deltaTestUtils.d.ts +18 -0
- package/dist/tools/__tests__/deltaTestUtils.js +26 -0
- package/dist/tools/accountTools.d.ts +37 -0
- package/dist/tools/accountTools.js +175 -0
- package/dist/tools/budgetTools.d.ts +10 -0
- package/dist/tools/budgetTools.js +68 -0
- package/dist/tools/categoryTools.d.ts +27 -0
- package/dist/tools/categoryTools.js +232 -0
- package/dist/tools/compareTransactions/formatter.d.ts +71 -0
- package/dist/tools/compareTransactions/formatter.js +97 -0
- package/dist/tools/compareTransactions/index.d.ts +30 -0
- package/dist/tools/compareTransactions/index.js +160 -0
- package/dist/tools/compareTransactions/matcher.d.ts +12 -0
- package/dist/tools/compareTransactions/matcher.js +140 -0
- package/dist/tools/compareTransactions/parser.d.ts +14 -0
- package/dist/tools/compareTransactions/parser.js +430 -0
- package/dist/tools/compareTransactions/types.d.ts +27 -0
- package/dist/tools/compareTransactions/types.js +1 -0
- package/dist/tools/compareTransactions.d.ts +1 -0
- package/dist/tools/compareTransactions.js +1 -0
- package/dist/tools/deltaFetcher.d.ts +22 -0
- package/dist/tools/deltaFetcher.js +137 -0
- package/dist/tools/deltaSupport.d.ts +20 -0
- package/dist/tools/deltaSupport.js +176 -0
- package/dist/tools/exportTransactions.d.ts +17 -0
- package/dist/tools/exportTransactions.js +191 -0
- package/dist/tools/monthTools.d.ts +16 -0
- package/dist/tools/monthTools.js +107 -0
- package/dist/tools/payeeTools.d.ts +17 -0
- package/dist/tools/payeeTools.js +82 -0
- package/dist/tools/reconcileAdapter.d.ts +25 -0
- package/dist/tools/reconcileAdapter.js +167 -0
- package/dist/tools/reconciliation/analyzer.d.ts +3 -0
- package/dist/tools/reconciliation/analyzer.js +567 -0
- package/dist/tools/reconciliation/executor.d.ts +94 -0
- package/dist/tools/reconciliation/executor.js +611 -0
- package/dist/tools/reconciliation/index.d.ts +54 -0
- package/dist/tools/reconciliation/index.js +249 -0
- package/dist/tools/reconciliation/matcher.d.ts +3 -0
- package/dist/tools/reconciliation/matcher.js +160 -0
- package/dist/tools/reconciliation/payeeNormalizer.d.ts +6 -0
- package/dist/tools/reconciliation/payeeNormalizer.js +77 -0
- package/dist/tools/reconciliation/recommendationEngine.d.ts +2 -0
- package/dist/tools/reconciliation/recommendationEngine.js +273 -0
- package/dist/tools/reconciliation/reportFormatter.d.ts +13 -0
- package/dist/tools/reconciliation/reportFormatter.js +214 -0
- package/dist/tools/reconciliation/types.d.ts +172 -0
- package/dist/tools/reconciliation/types.js +7 -0
- package/dist/tools/schemas/outputs/accountOutputs.d.ts +58 -0
- package/dist/tools/schemas/outputs/accountOutputs.js +24 -0
- package/dist/tools/schemas/outputs/budgetOutputs.d.ts +48 -0
- package/dist/tools/schemas/outputs/budgetOutputs.js +15 -0
- package/dist/tools/schemas/outputs/categoryOutputs.d.ts +93 -0
- package/dist/tools/schemas/outputs/categoryOutputs.js +37 -0
- package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +269 -0
- package/dist/tools/schemas/outputs/comparisonOutputs.js +181 -0
- package/dist/tools/schemas/outputs/index.d.ts +14 -0
- package/dist/tools/schemas/outputs/index.js +14 -0
- package/dist/tools/schemas/outputs/monthOutputs.d.ts +122 -0
- package/dist/tools/schemas/outputs/monthOutputs.js +51 -0
- package/dist/tools/schemas/outputs/payeeOutputs.d.ts +34 -0
- package/dist/tools/schemas/outputs/payeeOutputs.js +16 -0
- package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +1275 -0
- package/dist/tools/schemas/outputs/reconciliationOutputs.js +377 -0
- package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +717 -0
- package/dist/tools/schemas/outputs/transactionMutationOutputs.js +260 -0
- package/dist/tools/schemas/outputs/transactionOutputs.d.ts +98 -0
- package/dist/tools/schemas/outputs/transactionOutputs.js +49 -0
- package/dist/tools/schemas/outputs/utilityOutputs.d.ts +219 -0
- package/dist/tools/schemas/outputs/utilityOutputs.js +120 -0
- package/dist/tools/schemas/shared/commonOutputs.d.ts +24 -0
- package/dist/tools/schemas/shared/commonOutputs.js +27 -0
- package/dist/tools/toolCategories.d.ts +32 -0
- package/dist/tools/toolCategories.js +32 -0
- package/dist/tools/transactionTools.d.ts +315 -0
- package/dist/tools/transactionTools.js +1722 -0
- package/dist/tools/utilityTools.d.ts +10 -0
- package/dist/tools/utilityTools.js +56 -0
- package/dist/types/index.d.ts +20 -0
- package/dist/types/index.js +16 -0
- package/dist/types/toolAnnotations.d.ts +7 -0
- package/dist/types/toolAnnotations.js +1 -0
- package/dist/utils/amountUtils.d.ts +3 -0
- package/dist/utils/amountUtils.js +10 -0
- package/dist/utils/dateUtils.d.ts +9 -0
- package/dist/utils/dateUtils.js +43 -0
- package/dist/utils/money.d.ts +21 -0
- package/dist/utils/money.js +51 -0
- package/docs/README.md +72 -0
- package/docs/assets/examples/reconciliation-with-recommendations.json +68 -0
- package/docs/assets/schemas/reconciliation-v2.json +338 -0
- package/docs/getting-started/CONFIGURATION.md +175 -0
- package/docs/getting-started/INSTALLATION.md +333 -0
- package/docs/getting-started/QUICKSTART.md +282 -0
- package/docs/guides/ARCHITECTURE.md +650 -0
- package/docs/guides/DEPLOYMENT.md +189 -0
- package/docs/guides/INTEGRATION_TESTING.md +730 -0
- package/docs/guides/TESTING.md +591 -0
- package/docs/reconciliation-flow.md +83 -0
- package/docs/reference/API.md +1450 -0
- package/docs/reference/EXAMPLES.md +946 -0
- package/docs/reference/TOOLS.md +348 -0
- package/docs/reference/TROUBLESHOOTING.md +481 -0
- package/esbuild.config.mjs +68 -0
- package/eslint.config.js +49 -0
- package/fix-types.sh +17 -0
- package/meta.json +12550 -0
- package/package.json +105 -0
- package/package.json.tmp +105 -0
- package/scripts/analyze-bundle.mjs +41 -0
- package/scripts/create-pr-description.js +203 -0
- package/scripts/generate-mcpb.ps1 +96 -0
- package/scripts/run-domain-integration-tests.js +33 -0
- package/scripts/run-generate-mcpb.js +29 -0
- package/scripts/run-throttled-integration-tests.js +116 -0
- package/scripts/test-delta-params.mjs +140 -0
- package/scripts/test-recommendations.ts +53 -0
- package/scripts/tmpTransaction.ts +48 -0
- package/scripts/validate-env.js +122 -0
- package/scripts/verify-build.js +105 -0
- package/scripts/watch-and-restart.ps1 +50 -0
- package/src/__tests__/comprehensive.integration.test.ts +1196 -0
- package/src/__tests__/delta.performance.test.ts +80 -0
- package/src/__tests__/performance.test.ts +725 -0
- package/src/__tests__/setup.ts +449 -0
- package/src/__tests__/testRunner.ts +444 -0
- package/src/__tests__/testUtils.ts +563 -0
- package/src/__tests__/workflows.e2e.test.ts +1675 -0
- package/src/index.ts +124 -0
- package/src/server/.gitkeep +1 -0
- package/src/server/YNABMCPServer.ts +1188 -0
- package/src/server/__tests__/YNABMCPServer.integration.test.ts +903 -0
- package/src/server/__tests__/YNABMCPServer.test.ts +894 -0
- package/src/server/__tests__/budgetResolver.test.ts +425 -0
- package/src/server/__tests__/cacheManager.test.ts +880 -0
- package/src/server/__tests__/config.test.ts +166 -0
- package/src/server/__tests__/deltaCache.merge.test.ts +724 -0
- package/src/server/__tests__/deltaCache.swr.test.ts +168 -0
- package/src/server/__tests__/deltaCache.test.ts +774 -0
- package/src/server/__tests__/diagnostics.test.ts +823 -0
- package/src/server/__tests__/errorHandler.integration.test.ts +466 -0
- package/src/server/__tests__/errorHandler.test.ts +416 -0
- package/src/server/__tests__/prompts.test.ts +354 -0
- package/src/server/__tests__/rateLimiter.test.ts +314 -0
- package/src/server/__tests__/requestLogger.test.ts +408 -0
- package/src/server/__tests__/resources.test.ts +299 -0
- package/src/server/__tests__/security.integration.test.ts +426 -0
- package/src/server/__tests__/securityMiddleware.test.ts +449 -0
- package/src/server/__tests__/server-startup.integration.test.ts +477 -0
- package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -0
- package/src/server/__tests__/toolRegistry.test.ts +855 -0
- package/src/server/budgetResolver.ts +235 -0
- package/src/server/cacheManager.ts +503 -0
- package/src/server/config.ts +41 -0
- package/src/server/deltaCache.merge.ts +149 -0
- package/src/server/deltaCache.ts +341 -0
- package/src/server/diagnostics.ts +338 -0
- package/src/server/errorHandler.ts +756 -0
- package/src/server/prompts.ts +291 -0
- package/src/server/rateLimiter.ts +156 -0
- package/src/server/requestLogger.ts +344 -0
- package/src/server/resources.ts +168 -0
- package/src/server/responseFormatter.ts +51 -0
- package/src/server/securityMiddleware.ts +236 -0
- package/src/server/serverKnowledgeStore.ts +91 -0
- package/src/server/toolRegistry.ts +489 -0
- package/src/tools/.gitkeep +1 -0
- package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -0
- package/src/tools/__tests__/accountTools.integration.test.ts +117 -0
- package/src/tools/__tests__/accountTools.test.ts +653 -0
- package/src/tools/__tests__/budgetTools.delta.integration.test.ts +90 -0
- package/src/tools/__tests__/budgetTools.integration.test.ts +134 -0
- package/src/tools/__tests__/budgetTools.test.ts +423 -0
- package/src/tools/__tests__/categoryTools.delta.integration.test.ts +80 -0
- package/src/tools/__tests__/categoryTools.integration.test.ts +295 -0
- package/src/tools/__tests__/categoryTools.test.ts +622 -0
- package/src/tools/__tests__/compareTransactions/formatter.test.ts +486 -0
- package/src/tools/__tests__/compareTransactions/index.test.ts +383 -0
- package/src/tools/__tests__/compareTransactions/matcher.test.ts +410 -0
- package/src/tools/__tests__/compareTransactions/parser.test.ts +764 -0
- package/src/tools/__tests__/compareTransactions.test.ts +342 -0
- package/src/tools/__tests__/compareTransactions.window.test.ts +147 -0
- package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +76 -0
- package/src/tools/__tests__/deltaFetcher.test.ts +270 -0
- package/src/tools/__tests__/deltaSupport.test.ts +188 -0
- package/src/tools/__tests__/deltaTestUtils.ts +46 -0
- package/src/tools/__tests__/exportTransactions.test.ts +213 -0
- package/src/tools/__tests__/monthTools.delta.integration.test.ts +80 -0
- package/src/tools/__tests__/monthTools.integration.test.ts +174 -0
- package/src/tools/__tests__/monthTools.test.ts +523 -0
- package/src/tools/__tests__/payeeTools.delta.integration.test.ts +80 -0
- package/src/tools/__tests__/payeeTools.integration.test.ts +150 -0
- package/src/tools/__tests__/payeeTools.test.ts +445 -0
- package/src/tools/__tests__/transactionTools.integration.test.ts +762 -0
- package/src/tools/__tests__/transactionTools.test.ts +3521 -0
- package/src/tools/__tests__/utilityTools.integration.test.ts +128 -0
- package/src/tools/__tests__/utilityTools.test.ts +205 -0
- package/src/tools/accountTools.ts +283 -0
- package/src/tools/budgetTools.ts +112 -0
- package/src/tools/categoryTools.ts +366 -0
- package/src/tools/compareTransactions/formatter.ts +163 -0
- package/src/tools/compareTransactions/index.ts +228 -0
- package/src/tools/compareTransactions/matcher.ts +240 -0
- package/src/tools/compareTransactions/parser.ts +557 -0
- package/src/tools/compareTransactions/types.ts +60 -0
- package/src/tools/compareTransactions.ts +3 -0
- package/src/tools/deltaFetcher.ts +278 -0
- package/src/tools/deltaSupport.ts +293 -0
- package/src/tools/exportTransactions.ts +273 -0
- package/src/tools/monthTools.ts +164 -0
- package/src/tools/payeeTools.ts +140 -0
- package/src/tools/reconcileAdapter.ts +312 -0
- package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +122 -0
- package/src/tools/reconciliation/__tests__/adapter.test.ts +234 -0
- package/src/tools/reconciliation/__tests__/analyzer.test.ts +406 -0
- package/src/tools/reconciliation/__tests__/executor.integration.test.ts +366 -0
- package/src/tools/reconciliation/__tests__/executor.test.ts +779 -0
- package/src/tools/reconciliation/__tests__/matcher.test.ts +650 -0
- package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +278 -0
- package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +658 -0
- package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1000 -0
- package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +151 -0
- package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +573 -0
- package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +78 -0
- package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +47 -0
- package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +61 -0
- package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +49 -0
- package/src/tools/reconciliation/analyzer.ts +824 -0
- package/src/tools/reconciliation/executor.ts +880 -0
- package/src/tools/reconciliation/index.ts +400 -0
- package/src/tools/reconciliation/matcher.ts +269 -0
- package/src/tools/reconciliation/payeeNormalizer.ts +167 -0
- package/src/tools/reconciliation/recommendationEngine.ts +506 -0
- package/src/tools/reconciliation/reportFormatter.ts +363 -0
- package/src/tools/reconciliation/types.ts +314 -0
- package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +424 -0
- package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +310 -0
- package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +448 -0
- package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +519 -0
- package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +155 -0
- package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +288 -0
- package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +478 -0
- package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +370 -0
- package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +401 -0
- package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +213 -0
- package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +474 -0
- package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +333 -0
- package/src/tools/schemas/outputs/accountOutputs.ts +137 -0
- package/src/tools/schemas/outputs/budgetOutputs.ts +86 -0
- package/src/tools/schemas/outputs/categoryOutputs.ts +194 -0
- package/src/tools/schemas/outputs/comparisonOutputs.ts +600 -0
- package/src/tools/schemas/outputs/index.ts +270 -0
- package/src/tools/schemas/outputs/monthOutputs.ts +243 -0
- package/src/tools/schemas/outputs/payeeOutputs.ts +105 -0
- package/src/tools/schemas/outputs/reconciliationOutputs.ts +796 -0
- package/src/tools/schemas/outputs/transactionMutationOutputs.ts +758 -0
- package/src/tools/schemas/outputs/transactionOutputs.ts +243 -0
- package/src/tools/schemas/outputs/utilityOutputs.ts +411 -0
- package/src/tools/schemas/shared/commonOutputs.ts +140 -0
- package/src/tools/toolCategories.ts +140 -0
- package/src/tools/transactionTools.ts +2509 -0
- package/src/tools/utilityTools.ts +90 -0
- package/src/types/.gitkeep +1 -0
- package/src/types/__tests__/index.test.ts +52 -0
- package/src/types/index.ts +67 -0
- package/src/types/integration-tests.d.ts +35 -0
- package/src/types/toolAnnotations.ts +44 -0
- package/src/utils/__tests__/dateUtils.test.ts +170 -0
- package/src/utils/__tests__/money.test.ts +189 -0
- package/src/utils/amountUtils.ts +32 -0
- package/src/utils/dateUtils.ts +108 -0
- package/src/utils/money.ts +123 -0
- package/test-csv-sample.csv +28 -0
- package/test-exports/sample_bank_statement.csv +7 -0
- package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_09-04-53.json +23 -0
- package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_10-37-42.json +23 -0
- package/test-exports/ynab_account_e9ddc2a6_minimal_4items_2025-11-19_09-02-09.json +44 -0
- package/test-exports/ynab_account_e9ddc2a6_minimal_6items_2025-11-19_10-37-52.json +58 -0
- package/test-exports/ynab_since_2025-11-01_account_4c18e9f0_minimal_14items_2025-11-16_10-07-10.json +115 -0
- package/test-reconcile-autodetect.js +40 -0
- package/test-reconcile-tool.js +152 -0
- package/test-reconcile-with-csv.cjs +89 -0
- package/test-statement.csv +8 -0
- package/test_debug.js +47 -0
- package/test_simple.mjs +16 -0
- package/tsconfig.json +31 -0
- package/tsconfig.prod.json +18 -0
- package/vitest-reporters/split-json-reporter.ts +211 -0
- package/vitest.config.ts +96 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompts module for YNAB MCP Server
|
|
3
|
+
*
|
|
4
|
+
* Handles MCP prompt definitions and handlers.
|
|
5
|
+
* Extracted from YNABMCPServer to provide focused, testable prompt management.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Prompt argument definition
|
|
10
|
+
*/
|
|
11
|
+
export interface PromptArgument {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
required: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Prompt definition structure
|
|
19
|
+
*/
|
|
20
|
+
export interface PromptDefinition {
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
arguments: PromptArgument[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Prompt response structure
|
|
28
|
+
*/
|
|
29
|
+
export interface PromptResponse {
|
|
30
|
+
description: string;
|
|
31
|
+
messages: {
|
|
32
|
+
role: 'user' | 'assistant';
|
|
33
|
+
content: {
|
|
34
|
+
type: 'text';
|
|
35
|
+
text: string;
|
|
36
|
+
};
|
|
37
|
+
}[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Prompt handler function signature
|
|
42
|
+
*/
|
|
43
|
+
export type PromptHandler = (
|
|
44
|
+
name: string,
|
|
45
|
+
args: Record<string, unknown> | undefined,
|
|
46
|
+
) => Promise<PromptResponse>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Default prompt definitions
|
|
50
|
+
*/
|
|
51
|
+
const defaultPromptDefinitions: PromptDefinition[] = [
|
|
52
|
+
{
|
|
53
|
+
name: 'create-transaction',
|
|
54
|
+
description: 'Create a new transaction in YNAB',
|
|
55
|
+
arguments: [
|
|
56
|
+
{
|
|
57
|
+
name: 'budget_name',
|
|
58
|
+
description: 'Name of the budget (optional, uses first budget if not specified)',
|
|
59
|
+
required: false,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'account_name',
|
|
63
|
+
description: 'Name of the account',
|
|
64
|
+
required: true,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'amount',
|
|
68
|
+
description: 'Transaction amount (negative for expenses, positive for income)',
|
|
69
|
+
required: true,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'payee',
|
|
73
|
+
description: 'Who you paid or received money from',
|
|
74
|
+
required: true,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'category',
|
|
78
|
+
description: 'Budget category (optional)',
|
|
79
|
+
required: false,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'memo',
|
|
83
|
+
description: 'Additional notes (optional)',
|
|
84
|
+
required: false,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'budget-summary',
|
|
90
|
+
description: 'Get a summary of your budget status',
|
|
91
|
+
arguments: [
|
|
92
|
+
{
|
|
93
|
+
name: 'budget_name',
|
|
94
|
+
description: 'Name of the budget (optional, uses first budget if not specified)',
|
|
95
|
+
required: false,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'month',
|
|
99
|
+
description:
|
|
100
|
+
'Month to analyze (YYYY-MM format, optional, uses current month if not specified)',
|
|
101
|
+
required: false,
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'account-balances',
|
|
107
|
+
description: 'Check balances across all accounts',
|
|
108
|
+
arguments: [
|
|
109
|
+
{
|
|
110
|
+
name: 'budget_name',
|
|
111
|
+
description: 'Name of the budget (optional, uses first budget if not specified)',
|
|
112
|
+
required: false,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'account_type',
|
|
116
|
+
description: 'Filter by account type (checking, savings, creditCard, etc.)',
|
|
117
|
+
required: false,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Default prompt handlers
|
|
125
|
+
*/
|
|
126
|
+
const defaultPromptHandlers: Record<string, PromptHandler> = {
|
|
127
|
+
'create-transaction': async (_name, args) => {
|
|
128
|
+
const budgetName = args?.['budget_name'] || 'first available budget';
|
|
129
|
+
const accountName = args?.['account_name'] || '[ACCOUNT_NAME]';
|
|
130
|
+
const amount = args?.['amount'] || '[AMOUNT]';
|
|
131
|
+
const payee = args?.['payee'] || '[PAYEE]';
|
|
132
|
+
const category = args?.['category'] || '[CATEGORY]';
|
|
133
|
+
const memo = args?.['memo'] || '';
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
description: `Create a transaction for ${payee} in ${accountName}`,
|
|
137
|
+
messages: [
|
|
138
|
+
{
|
|
139
|
+
role: 'user',
|
|
140
|
+
content: {
|
|
141
|
+
type: 'text',
|
|
142
|
+
text: `Please create a transaction with the following details:
|
|
143
|
+
- Budget: ${budgetName}
|
|
144
|
+
- Account: ${accountName}
|
|
145
|
+
- Amount: $${amount}
|
|
146
|
+
- Payee: ${payee}
|
|
147
|
+
- Category: ${category}
|
|
148
|
+
- Memo: ${memo}
|
|
149
|
+
|
|
150
|
+
Use the appropriate YNAB MCP tools to:
|
|
151
|
+
1. First, list budgets to find the budget ID
|
|
152
|
+
2. List accounts for that budget to find the account ID
|
|
153
|
+
3. If a category is specified, list categories to find the category ID
|
|
154
|
+
4. Create the transaction with the correct amount in milliunits (multiply by 1000)
|
|
155
|
+
5. Confirm the transaction was created successfully`,
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
};
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
'budget-summary': async (_name, args) => {
|
|
163
|
+
const summaryBudget = args?.['budget_name'] || 'first available budget';
|
|
164
|
+
const month = args?.['month'] || 'current month';
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
description: `Get budget summary for ${summaryBudget}`,
|
|
168
|
+
messages: [
|
|
169
|
+
{
|
|
170
|
+
role: 'user',
|
|
171
|
+
content: {
|
|
172
|
+
type: 'text',
|
|
173
|
+
text: `Please provide a comprehensive budget summary for ${summaryBudget} (${month}):
|
|
174
|
+
|
|
175
|
+
IMPORTANT: In YNAB, understand these key fields:
|
|
176
|
+
- budgeted: Amount assigned to the category this month
|
|
177
|
+
- activity: Spending/income in the category this month (negative = spending)
|
|
178
|
+
- balance: Available amount in the category = previous balance + budgeted + activity
|
|
179
|
+
- OVERSPENDING occurs when balance < 0 (Available goes negative), NOT when spending > budgeted for the month
|
|
180
|
+
|
|
181
|
+
SPENDING TRENDS: The analysis uses linear regression over multiple months to detect real spending patterns. Each trend includes:
|
|
182
|
+
- explanation: User-friendly description of what the trend means
|
|
183
|
+
- reliability_score: Confidence level (0-100%) indicating how reliable the trend is
|
|
184
|
+
- data_points: Number of months used in the analysis
|
|
185
|
+
Focus on trends with high reliability scores for actionable insights.
|
|
186
|
+
|
|
187
|
+
BUDGET OPTIMIZATION: The system provides three types of optimization insights:
|
|
188
|
+
1. "Consistently Under-Spent Categories" - Based on multi-month historical trends (reliable patterns)
|
|
189
|
+
2. "Categories Over Monthly Assignment" - Current month only (spending > budgeted but Available still positive)
|
|
190
|
+
3. "Large Unused Category Balances" - Categories with substantial unused funds
|
|
191
|
+
Distinguish between current-month patterns vs historical trends when presenting insights.
|
|
192
|
+
|
|
193
|
+
1. List all budgets and select the appropriate one
|
|
194
|
+
2. Get monthly data for ${month}
|
|
195
|
+
3. List categories to show budget vs actual spending
|
|
196
|
+
4. Provide insights on:
|
|
197
|
+
- Total budgeted vs actual spending
|
|
198
|
+
- Categories where Available balance is negative (true overspending - when the category's balance field is < 0)
|
|
199
|
+
- Categories where spending exceeded this month's assignment (but still have positive Available balance)
|
|
200
|
+
- Available money to budget
|
|
201
|
+
- Any true overspending where categories went into the red (negative Available balance)
|
|
202
|
+
|
|
203
|
+
Format the response in a clear, easy-to-read summary.`,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
};
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
'account-balances': async (_name, args) => {
|
|
211
|
+
const balanceBudget = args?.['budget_name'] || 'first available budget';
|
|
212
|
+
const accountType = args?.['account_type'] || 'all accounts';
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
description: `Check account balances for ${accountType}`,
|
|
216
|
+
messages: [
|
|
217
|
+
{
|
|
218
|
+
role: 'user',
|
|
219
|
+
content: {
|
|
220
|
+
type: 'text',
|
|
221
|
+
text: `Please show account balances for ${balanceBudget}:
|
|
222
|
+
|
|
223
|
+
1. List all budgets and select the appropriate one
|
|
224
|
+
2. List accounts for that budget
|
|
225
|
+
3. Filter by account type: ${accountType}
|
|
226
|
+
4. Show balances in a clear format with:
|
|
227
|
+
- Account name and type
|
|
228
|
+
- Current balance
|
|
229
|
+
- Cleared vs uncleared amounts
|
|
230
|
+
- Total by account type
|
|
231
|
+
- Net worth summary (assets - liabilities)
|
|
232
|
+
|
|
233
|
+
Convert milliunits to dollars for easy reading.`,
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
};
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* PromptManager class that handles prompt registration and request handling
|
|
243
|
+
*/
|
|
244
|
+
export class PromptManager {
|
|
245
|
+
private promptDefinitions: PromptDefinition[];
|
|
246
|
+
private promptHandlers: Record<string, PromptHandler>;
|
|
247
|
+
|
|
248
|
+
constructor() {
|
|
249
|
+
this.promptDefinitions = [...defaultPromptDefinitions];
|
|
250
|
+
this.promptHandlers = { ...defaultPromptHandlers };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Register a new prompt with its handler at runtime
|
|
255
|
+
*/
|
|
256
|
+
registerPrompt(definition: PromptDefinition, handler: PromptHandler): void {
|
|
257
|
+
this.promptDefinitions.push(definition);
|
|
258
|
+
this.promptHandlers[definition.name] = handler;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Returns list of available prompts for MCP prompt listing
|
|
263
|
+
*/
|
|
264
|
+
listPrompts(): { prompts: PromptDefinition[] } {
|
|
265
|
+
return {
|
|
266
|
+
prompts: this.promptDefinitions,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Handles prompt get requests
|
|
272
|
+
*/
|
|
273
|
+
async getPrompt(
|
|
274
|
+
name: string,
|
|
275
|
+
args: Record<string, unknown> | undefined,
|
|
276
|
+
): Promise<PromptResponse> {
|
|
277
|
+
const handler = this.promptHandlers[name];
|
|
278
|
+
if (!handler) {
|
|
279
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const definition = this.promptDefinitions.find((p) => p.name === name);
|
|
283
|
+
if (!definition) {
|
|
284
|
+
throw new Error(`Prompt definition not found: ${name}`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Let handlers deal with missing arguments - they provide placeholders when args are missing
|
|
288
|
+
|
|
289
|
+
return await handler(name, args);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate limiting implementation for YNAB API compliance
|
|
3
|
+
* YNAB API has a rate limit of 200 requests per hour per access token
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface RateLimitConfig {
|
|
7
|
+
maxRequests: number;
|
|
8
|
+
windowMs: number;
|
|
9
|
+
enableLogging?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface RateLimitInfo {
|
|
13
|
+
remaining: number;
|
|
14
|
+
resetTime: Date;
|
|
15
|
+
isLimited: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Simple in-memory rate limiter for YNAB API compliance
|
|
20
|
+
*/
|
|
21
|
+
export class RateLimiter {
|
|
22
|
+
private requests: Map<string, number[]> = new Map();
|
|
23
|
+
private config: RateLimitConfig;
|
|
24
|
+
|
|
25
|
+
constructor(config: Partial<RateLimitConfig> = {}) {
|
|
26
|
+
this.config = {
|
|
27
|
+
maxRequests: 200, // YNAB API limit
|
|
28
|
+
windowMs: 60 * 60 * 1000, // 1 hour
|
|
29
|
+
enableLogging: false,
|
|
30
|
+
...config,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Check if a request is allowed for the given identifier (typically access token hash)
|
|
36
|
+
*/
|
|
37
|
+
isAllowed(identifier: string): RateLimitInfo {
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
const windowStart = now - this.config.windowMs;
|
|
40
|
+
|
|
41
|
+
// Get existing requests for this identifier
|
|
42
|
+
let requests = this.requests.get(identifier) || [];
|
|
43
|
+
|
|
44
|
+
// Remove requests outside the current window
|
|
45
|
+
requests = requests.filter((timestamp) => timestamp > windowStart);
|
|
46
|
+
|
|
47
|
+
// Update the requests array
|
|
48
|
+
this.requests.set(identifier, requests);
|
|
49
|
+
|
|
50
|
+
const remaining = Math.max(0, this.config.maxRequests - requests.length);
|
|
51
|
+
const resetTime = new Date(now + this.config.windowMs);
|
|
52
|
+
const isLimited = requests.length >= this.config.maxRequests;
|
|
53
|
+
|
|
54
|
+
if (this.config.enableLogging) {
|
|
55
|
+
console.error(
|
|
56
|
+
`Rate limit check for ${this.hashIdentifier(identifier)}: ${requests.length}/${this.config.maxRequests} requests, remaining: ${remaining}, limited: ${isLimited}`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
remaining,
|
|
62
|
+
resetTime,
|
|
63
|
+
isLimited,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Record a request for the given identifier
|
|
69
|
+
*/
|
|
70
|
+
recordRequest(identifier: string): void {
|
|
71
|
+
const now = Date.now();
|
|
72
|
+
const requests = this.requests.get(identifier) || [];
|
|
73
|
+
|
|
74
|
+
requests.push(now);
|
|
75
|
+
this.requests.set(identifier, requests);
|
|
76
|
+
|
|
77
|
+
if (this.config.enableLogging) {
|
|
78
|
+
console.error(
|
|
79
|
+
`Recorded request for ${this.hashIdentifier(identifier)}: ${requests.length}/${this.config.maxRequests} requests`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get current rate limit status for an identifier
|
|
86
|
+
*/
|
|
87
|
+
getStatus(identifier: string): RateLimitInfo {
|
|
88
|
+
return this.isAllowed(identifier);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Reset rate limit for a specific identifier (useful for testing)
|
|
93
|
+
*/
|
|
94
|
+
reset(identifier?: string): void {
|
|
95
|
+
if (identifier) {
|
|
96
|
+
this.requests.delete(identifier);
|
|
97
|
+
} else {
|
|
98
|
+
this.requests.clear();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Clean up old requests (should be called periodically)
|
|
104
|
+
*/
|
|
105
|
+
cleanup(): void {
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
const windowStart = now - this.config.windowMs;
|
|
108
|
+
|
|
109
|
+
for (const [identifier, requests] of this.requests.entries()) {
|
|
110
|
+
const validRequests = requests.filter((timestamp) => timestamp > windowStart);
|
|
111
|
+
|
|
112
|
+
if (validRequests.length === 0) {
|
|
113
|
+
this.requests.delete(identifier);
|
|
114
|
+
} else {
|
|
115
|
+
this.requests.set(identifier, validRequests);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Hash identifier for logging (to avoid exposing sensitive tokens)
|
|
122
|
+
*/
|
|
123
|
+
private hashIdentifier(identifier: string): string {
|
|
124
|
+
// Simple hash for logging purposes - not cryptographically secure
|
|
125
|
+
let hash = 0;
|
|
126
|
+
for (let i = 0; i < identifier.length; i++) {
|
|
127
|
+
const char = identifier.charCodeAt(i);
|
|
128
|
+
hash = (hash << 5) - hash + char;
|
|
129
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
130
|
+
}
|
|
131
|
+
return `token_${Math.abs(hash).toString(16)}`;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Rate limiting error class
|
|
137
|
+
*/
|
|
138
|
+
export class RateLimitError extends Error {
|
|
139
|
+
constructor(
|
|
140
|
+
message: string,
|
|
141
|
+
|
|
142
|
+
public readonly resetTime: Date,
|
|
143
|
+
|
|
144
|
+
public readonly remaining: number = 0,
|
|
145
|
+
) {
|
|
146
|
+
super(message);
|
|
147
|
+
this.name = 'RateLimitError';
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Global rate limiter instance
|
|
153
|
+
*/
|
|
154
|
+
export const globalRateLimiter = new RateLimiter({
|
|
155
|
+
enableLogging: process.env['NODE_ENV'] !== 'production',
|
|
156
|
+
});
|