@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.
- package/CHANGELOG.md +17 -0
- package/CLAUDE.md +87 -8
- package/bin/ynab-mcp-server.cjs +2 -2
- package/bin/ynab-mcp-server.js +3 -3
- package/biome.json +39 -0
- package/dist/bundle/index.cjs +67 -67
- package/dist/index.d.ts +1 -1
- package/dist/index.js +27 -27
- package/dist/server/YNABMCPServer.d.ts +3 -4
- package/dist/server/YNABMCPServer.js +111 -116
- package/dist/server/budgetResolver.d.ts +6 -5
- package/dist/server/budgetResolver.js +46 -36
- package/dist/server/cacheKeys.js +6 -6
- package/dist/server/cacheManager.js +14 -11
- package/dist/server/completions.d.ts +2 -2
- package/dist/server/completions.js +20 -15
- package/dist/server/config.d.ts +10 -5
- package/dist/server/config.js +24 -7
- package/dist/server/deltaCache.d.ts +2 -2
- package/dist/server/deltaCache.js +22 -16
- package/dist/server/deltaCache.merge.d.ts +2 -2
- package/dist/server/diagnostics.d.ts +4 -4
- package/dist/server/diagnostics.js +38 -32
- package/dist/server/errorHandler.d.ts +5 -12
- package/dist/server/errorHandler.js +219 -217
- package/dist/server/prompts.d.ts +2 -2
- package/dist/server/prompts.js +45 -45
- package/dist/server/rateLimiter.js +4 -4
- package/dist/server/requestLogger.d.ts +1 -1
- package/dist/server/requestLogger.js +40 -35
- package/dist/server/resources.d.ts +3 -3
- package/dist/server/resources.js +55 -52
- package/dist/server/responseFormatter.js +6 -6
- package/dist/server/securityMiddleware.d.ts +2 -2
- package/dist/server/securityMiddleware.js +22 -20
- package/dist/server/serverKnowledgeStore.js +1 -1
- package/dist/server/toolRegistry.d.ts +3 -3
- package/dist/server/toolRegistry.js +47 -40
- package/dist/tools/__tests__/deltaTestUtils.d.ts +3 -3
- package/dist/tools/__tests__/deltaTestUtils.js +2 -2
- package/dist/tools/accountTools.d.ts +9 -8
- package/dist/tools/accountTools.js +47 -47
- package/dist/tools/adapters.d.ts +13 -8
- package/dist/tools/adapters.js +21 -11
- package/dist/tools/budgetTools.d.ts +8 -7
- package/dist/tools/budgetTools.js +22 -22
- package/dist/tools/categoryTools.d.ts +9 -8
- package/dist/tools/categoryTools.js +68 -59
- package/dist/tools/compareTransactions/formatter.d.ts +3 -3
- package/dist/tools/compareTransactions/formatter.js +9 -9
- package/dist/tools/compareTransactions/index.d.ts +6 -6
- package/dist/tools/compareTransactions/index.js +58 -43
- package/dist/tools/compareTransactions/matcher.d.ts +1 -1
- package/dist/tools/compareTransactions/matcher.js +28 -15
- package/dist/tools/compareTransactions/parser.d.ts +2 -2
- package/dist/tools/compareTransactions/parser.js +144 -138
- package/dist/tools/compareTransactions/types.d.ts +4 -4
- package/dist/tools/compareTransactions.d.ts +1 -1
- package/dist/tools/compareTransactions.js +1 -1
- package/dist/tools/deltaFetcher.d.ts +2 -2
- package/dist/tools/deltaFetcher.js +16 -15
- package/dist/tools/deltaSupport.d.ts +4 -4
- package/dist/tools/deltaSupport.js +35 -41
- package/dist/tools/exportTransactions.d.ts +5 -4
- package/dist/tools/exportTransactions.js +61 -59
- package/dist/tools/monthTools.d.ts +7 -6
- package/dist/tools/monthTools.js +31 -29
- package/dist/tools/payeeTools.d.ts +7 -6
- package/dist/tools/payeeTools.js +28 -28
- package/dist/tools/reconcileAdapter.d.ts +2 -2
- package/dist/tools/reconcileAdapter.js +21 -11
- package/dist/tools/reconciliation/analyzer.d.ts +4 -4
- package/dist/tools/reconciliation/analyzer.js +136 -57
- package/dist/tools/reconciliation/csvParser.d.ts +3 -3
- package/dist/tools/reconciliation/csvParser.js +128 -104
- package/dist/tools/reconciliation/executor.d.ts +4 -4
- package/dist/tools/reconciliation/executor.js +148 -109
- package/dist/tools/reconciliation/index.d.ts +10 -10
- package/dist/tools/reconciliation/index.js +96 -83
- package/dist/tools/reconciliation/matcher.d.ts +3 -3
- package/dist/tools/reconciliation/matcher.js +17 -16
- package/dist/tools/reconciliation/payeeNormalizer.js +19 -8
- package/dist/tools/reconciliation/recommendationEngine.d.ts +1 -1
- package/dist/tools/reconciliation/recommendationEngine.js +40 -40
- package/dist/tools/reconciliation/reportFormatter.d.ts +2 -2
- package/dist/tools/reconciliation/reportFormatter.js +79 -54
- package/dist/tools/reconciliation/signDetector.d.ts +1 -1
- package/dist/tools/reconciliation/types.d.ts +19 -16
- package/dist/tools/reconciliation/ynabAdapter.d.ts +2 -2
- package/dist/tools/schemas/common.d.ts +1 -1
- package/dist/tools/schemas/common.js +1 -1
- package/dist/tools/schemas/outputs/accountOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/accountOutputs.js +24 -18
- package/dist/tools/schemas/outputs/budgetOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/budgetOutputs.js +14 -11
- package/dist/tools/schemas/outputs/categoryOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/categoryOutputs.js +49 -29
- package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/comparisonOutputs.js +12 -12
- package/dist/tools/schemas/outputs/index.d.ts +14 -14
- package/dist/tools/schemas/outputs/index.js +14 -14
- package/dist/tools/schemas/outputs/monthOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/monthOutputs.js +56 -41
- package/dist/tools/schemas/outputs/payeeOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/payeeOutputs.js +10 -10
- package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +2 -2
- package/dist/tools/schemas/outputs/reconciliationOutputs.js +45 -45
- package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/transactionMutationOutputs.js +28 -22
- package/dist/tools/schemas/outputs/transactionOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/transactionOutputs.js +43 -35
- package/dist/tools/schemas/outputs/utilityOutputs.d.ts +1 -1
- package/dist/tools/schemas/outputs/utilityOutputs.js +5 -3
- package/dist/tools/schemas/shared/commonOutputs.d.ts +1 -1
- package/dist/tools/schemas/shared/commonOutputs.js +15 -9
- package/dist/tools/transactionReadTools.d.ts +11 -0
- package/dist/tools/transactionReadTools.js +202 -0
- package/dist/tools/transactionSchemas.d.ts +309 -0
- package/dist/tools/transactionSchemas.js +235 -0
- package/dist/tools/transactionTools.d.ts +6 -302
- package/dist/tools/transactionTools.js +7 -2054
- package/dist/tools/transactionUtils.d.ts +31 -0
- package/dist/tools/transactionUtils.js +364 -0
- package/dist/tools/transactionWriteTools.d.ts +20 -0
- package/dist/tools/transactionWriteTools.js +1342 -0
- package/dist/tools/utilityTools.d.ts +5 -4
- package/dist/tools/utilityTools.js +11 -11
- package/dist/types/index.d.ts +7 -7
- package/dist/types/index.js +6 -6
- package/dist/types/reconciliation.d.ts +1 -1
- package/dist/types/toolRegistration.d.ts +14 -12
- package/dist/utils/amountUtils.js +1 -1
- package/dist/utils/dateUtils.js +4 -4
- package/dist/utils/errors.d.ts +3 -3
- package/dist/utils/errors.js +4 -4
- package/dist/utils/money.d.ts +2 -2
- package/dist/utils/money.js +8 -8
- package/dist/utils/validationError.d.ts +1 -1
- package/dist/utils/validationError.js +1 -1
- package/docs/assets/examples/reconciliation-with-recommendations.json +66 -66
- package/docs/assets/schemas/reconciliation-v2.json +360 -336
- package/docs/plans/2025-12-25-transaction-tools-refactor-design.md +211 -0
- package/docs/plans/2025-12-25-transaction-tools-refactor.md +905 -0
- package/esbuild.config.mjs +53 -50
- package/meta.json +12548 -12548
- package/package.json +98 -109
- package/scripts/analyze-bundle.mjs +33 -30
- package/scripts/create-pr-description.js +169 -120
- package/scripts/run-all-tests.js +205 -0
- package/scripts/run-domain-integration-tests.js +28 -18
- package/scripts/run-generate-mcpb.js +19 -17
- package/scripts/run-throttled-integration-tests.js +92 -83
- package/scripts/test-delta-params.mjs +149 -120
- package/scripts/test-recommendations.ts +36 -32
- package/scripts/tmpTransaction.ts +80 -43
- package/scripts/validate-env.js +98 -91
- package/scripts/verify-build.js +78 -76
- package/src/__tests__/comprehensive.integration.test.ts +1281 -1154
- package/src/__tests__/performance.test.ts +723 -671
- package/src/__tests__/setup.ts +442 -395
- package/src/__tests__/smoke.e2e.test.ts +41 -39
- package/src/__tests__/testRunner.ts +314 -295
- package/src/__tests__/testUtils.ts +456 -364
- package/src/__tests__/tools/reconciliation/csvParser.integration.test.ts +109 -107
- package/src/__tests__/tools/reconciliation/real-world.integration.test.ts +41 -41
- package/src/index.ts +68 -59
- package/src/server/CLAUDE.md +480 -0
- package/src/server/YNABMCPServer.ts +821 -794
- package/src/server/__tests__/YNABMCPServer.integration.test.ts +929 -893
- package/src/server/__tests__/YNABMCPServer.test.ts +903 -899
- package/src/server/__tests__/budgetResolver.test.ts +466 -423
- package/src/server/__tests__/cacheManager.test.ts +891 -874
- package/src/server/__tests__/completions.integration.test.ts +115 -106
- package/src/server/__tests__/completions.test.ts +334 -313
- package/src/server/__tests__/config.test.ts +98 -86
- package/src/server/__tests__/deltaCache.merge.test.ts +774 -703
- package/src/server/__tests__/deltaCache.swr.test.ts +198 -153
- package/src/server/__tests__/deltaCache.test.ts +946 -759
- package/src/server/__tests__/diagnostics.test.ts +825 -792
- package/src/server/__tests__/errorHandler.integration.test.ts +512 -462
- package/src/server/__tests__/errorHandler.test.ts +402 -397
- package/src/server/__tests__/prompts.test.ts +424 -347
- package/src/server/__tests__/rateLimiter.test.ts +313 -309
- package/src/server/__tests__/requestLogger.test.ts +443 -403
- package/src/server/__tests__/resources.template.test.ts +196 -185
- package/src/server/__tests__/resources.test.ts +294 -288
- package/src/server/__tests__/security.integration.test.ts +487 -421
- package/src/server/__tests__/securityMiddleware.test.ts +519 -444
- package/src/server/__tests__/server-startup.integration.test.ts +509 -490
- package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -173
- package/src/server/__tests__/toolRegistration.test.ts +239 -210
- package/src/server/__tests__/toolRegistry.test.ts +907 -845
- package/src/server/budgetResolver.ts +221 -181
- package/src/server/cacheKeys.ts +6 -6
- package/src/server/cacheManager.ts +498 -484
- package/src/server/completions.ts +267 -243
- package/src/server/config.ts +35 -14
- package/src/server/deltaCache.merge.ts +146 -128
- package/src/server/deltaCache.ts +352 -309
- package/src/server/diagnostics.ts +257 -242
- package/src/server/errorHandler.ts +747 -744
- package/src/server/prompts.ts +181 -176
- package/src/server/rateLimiter.ts +131 -129
- package/src/server/requestLogger.ts +350 -322
- package/src/server/resources.ts +442 -374
- package/src/server/responseFormatter.ts +41 -37
- package/src/server/securityMiddleware.ts +223 -205
- package/src/server/serverKnowledgeStore.ts +67 -67
- package/src/server/toolRegistry.ts +508 -474
- package/src/tools/CLAUDE.md +604 -0
- package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -111
- package/src/tools/__tests__/accountTools.integration.test.ts +129 -111
- package/src/tools/__tests__/accountTools.test.ts +685 -638
- package/src/tools/__tests__/adapters.test.ts +142 -108
- package/src/tools/__tests__/budgetTools.delta.integration.test.ts +73 -73
- package/src/tools/__tests__/budgetTools.integration.test.ts +132 -124
- package/src/tools/__tests__/budgetTools.test.ts +442 -413
- package/src/tools/__tests__/categoryTools.delta.integration.test.ts +76 -68
- package/src/tools/__tests__/categoryTools.integration.test.ts +314 -288
- package/src/tools/__tests__/categoryTools.test.ts +656 -625
- package/src/tools/__tests__/compareTransactions/formatter.test.ts +535 -462
- package/src/tools/__tests__/compareTransactions/index.test.ts +378 -358
- package/src/tools/__tests__/compareTransactions/matcher.test.ts +497 -398
- package/src/tools/__tests__/compareTransactions/parser.test.ts +765 -747
- package/src/tools/__tests__/compareTransactions.test.ts +352 -332
- package/src/tools/__tests__/compareTransactions.window.test.ts +150 -146
- package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +69 -65
- package/src/tools/__tests__/deltaFetcher.test.ts +325 -265
- package/src/tools/__tests__/deltaSupport.test.ts +211 -184
- package/src/tools/__tests__/deltaTestUtils.ts +37 -33
- package/src/tools/__tests__/exportTransactions.test.ts +205 -200
- package/src/tools/__tests__/monthTools.delta.integration.test.ts +68 -68
- package/src/tools/__tests__/monthTools.integration.test.ts +178 -166
- package/src/tools/__tests__/monthTools.test.ts +561 -512
- package/src/tools/__tests__/payeeTools.delta.integration.test.ts +68 -68
- package/src/tools/__tests__/payeeTools.integration.test.ts +158 -142
- package/src/tools/__tests__/payeeTools.test.ts +486 -434
- package/src/tools/__tests__/transactionSchemas.test.ts +1204 -0
- package/src/tools/__tests__/transactionTools.integration.test.ts +875 -825
- package/src/tools/__tests__/transactionTools.test.ts +4923 -4366
- package/src/tools/__tests__/transactionUtils.test.ts +1016 -0
- package/src/tools/__tests__/utilityTools.integration.test.ts +32 -32
- package/src/tools/__tests__/utilityTools.test.ts +68 -58
- package/src/tools/accountTools.ts +293 -271
- package/src/tools/adapters.ts +120 -63
- package/src/tools/budgetTools.ts +121 -116
- package/src/tools/categoryTools.ts +379 -339
- package/src/tools/compareTransactions/formatter.ts +131 -119
- package/src/tools/compareTransactions/index.ts +249 -214
- package/src/tools/compareTransactions/matcher.ts +259 -209
- package/src/tools/compareTransactions/parser.ts +517 -487
- package/src/tools/compareTransactions/types.ts +38 -38
- package/src/tools/compareTransactions.ts +1 -1
- package/src/tools/deltaFetcher.ts +281 -260
- package/src/tools/deltaSupport.ts +264 -259
- package/src/tools/exportTransactions.ts +230 -218
- package/src/tools/monthTools.ts +180 -165
- package/src/tools/payeeTools.ts +152 -140
- package/src/tools/reconcileAdapter.ts +297 -246
- package/src/tools/reconciliation/CLAUDE.md +506 -0
- package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +135 -112
- package/src/tools/reconciliation/__tests__/adapter.test.ts +249 -227
- package/src/tools/reconciliation/__tests__/analyzer.test.ts +408 -335
- package/src/tools/reconciliation/__tests__/csvParser.test.ts +71 -69
- package/src/tools/reconciliation/__tests__/executor.integration.test.ts +348 -323
- package/src/tools/reconciliation/__tests__/executor.progress.test.ts +503 -457
- package/src/tools/reconciliation/__tests__/executor.test.ts +898 -831
- package/src/tools/reconciliation/__tests__/matcher.test.ts +667 -663
- package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +296 -276
- package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +692 -624
- package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1008 -986
- package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +187 -146
- package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +583 -530
- package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +75 -71
- package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +70 -58
- package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +102 -88
- package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +58 -43
- package/src/tools/reconciliation/__tests__/signDetector.test.ts +209 -206
- package/src/tools/reconciliation/__tests__/ynabAdapter.test.ts +66 -60
- package/src/tools/reconciliation/analyzer.ts +582 -406
- package/src/tools/reconciliation/csvParser.ts +656 -609
- package/src/tools/reconciliation/executor.ts +1290 -1128
- package/src/tools/reconciliation/index.ts +580 -528
- package/src/tools/reconciliation/matcher.ts +256 -240
- package/src/tools/reconciliation/payeeNormalizer.ts +92 -78
- package/src/tools/reconciliation/recommendationEngine.ts +357 -345
- package/src/tools/reconciliation/reportFormatter.ts +349 -276
- package/src/tools/reconciliation/signDetector.ts +89 -83
- package/src/tools/reconciliation/types.ts +164 -153
- package/src/tools/reconciliation/ynabAdapter.ts +17 -15
- package/src/tools/schemas/CLAUDE.md +546 -0
- package/src/tools/schemas/common.ts +1 -1
- package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +410 -409
- package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +305 -299
- package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +431 -430
- package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +510 -495
- package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +179 -153
- package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +293 -254
- package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +457 -457
- package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +362 -356
- package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +402 -399
- package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +225 -211
- package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +457 -454
- package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +316 -315
- package/src/tools/schemas/outputs/accountOutputs.ts +40 -34
- package/src/tools/schemas/outputs/budgetOutputs.ts +24 -19
- package/src/tools/schemas/outputs/categoryOutputs.ts +76 -56
- package/src/tools/schemas/outputs/comparisonOutputs.ts +192 -169
- package/src/tools/schemas/outputs/index.ts +163 -163
- package/src/tools/schemas/outputs/monthOutputs.ts +95 -80
- package/src/tools/schemas/outputs/payeeOutputs.ts +18 -18
- package/src/tools/schemas/outputs/reconciliationOutputs.ts +386 -373
- package/src/tools/schemas/outputs/transactionMutationOutputs.ts +259 -231
- package/src/tools/schemas/outputs/transactionOutputs.ts +81 -71
- package/src/tools/schemas/outputs/utilityOutputs.ts +90 -84
- package/src/tools/schemas/shared/commonOutputs.ts +27 -19
- package/src/tools/toolCategories.ts +114 -114
- package/src/tools/transactionReadTools.ts +327 -0
- package/src/tools/transactionSchemas.ts +484 -0
- package/src/tools/transactionTools.ts +107 -2990
- package/src/tools/transactionUtils.ts +621 -0
- package/src/tools/transactionWriteTools.ts +2110 -0
- package/src/tools/utilityTools.ts +46 -41
- package/src/types/CLAUDE.md +477 -0
- package/src/types/__tests__/index.test.ts +51 -51
- package/src/types/index.ts +43 -39
- package/src/types/integration-tests.d.ts +26 -26
- package/src/types/reconciliation.ts +29 -29
- package/src/types/toolAnnotations.ts +30 -30
- package/src/types/toolRegistration.ts +43 -32
- package/src/utils/CLAUDE.md +508 -0
- package/src/utils/__tests__/dateUtils.test.ts +174 -168
- package/src/utils/__tests__/money.test.ts +193 -187
- package/src/utils/amountUtils.ts +5 -5
- package/src/utils/baseError.ts +5 -5
- package/src/utils/dateUtils.ts +29 -26
- package/src/utils/errors.ts +14 -14
- package/src/utils/money.ts +66 -52
- package/src/utils/validationError.ts +1 -1
- package/tsconfig.json +29 -29
- package/tsconfig.prod.json +16 -16
- package/vitest-reporters/split-json-reporter.ts +247 -204
- package/vitest.config.ts +99 -95
- package/.prettierignore +0 -10
- package/.prettierrc.json +0 -10
- 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 =
|
|
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 =
|
|
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:
|
|
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
|
|
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(
|
|
84
|
+
: this.getErrorSuggestions(SecurityErrorCode.VALIDATION_ERROR, context);
|
|
82
85
|
return {
|
|
83
86
|
error: {
|
|
84
|
-
code:
|
|
87
|
+
code: SecurityErrorCode.VALIDATION_ERROR,
|
|
85
88
|
message: error.message,
|
|
86
|
-
userMessage: this.getUserFriendlyMessage(
|
|
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 ===
|
|
143
|
+
else if (typeof error === "string") {
|
|
141
144
|
errorMessage = error;
|
|
142
145
|
}
|
|
143
|
-
else if (error && typeof error ===
|
|
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:
|
|
160
|
+
code: SecurityErrorCode.UNKNOWN_ERROR,
|
|
158
161
|
message: this.getGenericErrorMessage(context),
|
|
159
162
|
userMessage: this.getUserFriendlyGenericMessage(context),
|
|
160
163
|
suggestions: [
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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(
|
|
172
|
-
return
|
|
174
|
+
if (message.includes("401") || message.includes("unauthorized")) {
|
|
175
|
+
return YNABErrorCode.UNAUTHORIZED;
|
|
173
176
|
}
|
|
174
|
-
if (message.includes(
|
|
175
|
-
return
|
|
177
|
+
if (message.includes("403") || message.includes("forbidden")) {
|
|
178
|
+
return YNABErrorCode.FORBIDDEN;
|
|
176
179
|
}
|
|
177
|
-
if (message.includes(
|
|
178
|
-
return
|
|
180
|
+
if (message.includes("404") || message.includes("not found")) {
|
|
181
|
+
return YNABErrorCode.NOT_FOUND;
|
|
179
182
|
}
|
|
180
|
-
if (message.includes(
|
|
181
|
-
return
|
|
183
|
+
if (message.includes("429") || message.includes("too many requests")) {
|
|
184
|
+
return YNABErrorCode.TOO_MANY_REQUESTS;
|
|
182
185
|
}
|
|
183
|
-
if (message.includes(
|
|
184
|
-
return
|
|
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
|
|
191
|
-
return
|
|
192
|
-
case
|
|
193
|
-
return
|
|
194
|
-
case
|
|
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
|
|
199
|
+
case YNABErrorCode.NOT_FOUND:
|
|
197
200
|
return this.getUserFriendlyNotFoundMessage(context);
|
|
198
|
-
case
|
|
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
|
|
203
|
+
case YNABErrorCode.INTERNAL_SERVER_ERROR:
|
|
201
204
|
return "YNAB's servers are having issues. Please try again in a few minutes.";
|
|
202
|
-
case
|
|
203
|
-
return
|
|
204
|
-
case
|
|
205
|
-
return
|
|
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
|
|
215
|
+
case YNABErrorCode.BAD_REQUEST:
|
|
213
216
|
return [
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
|
221
|
+
case YNABErrorCode.UNAUTHORIZED:
|
|
219
222
|
return [
|
|
220
|
-
|
|
221
|
-
|
|
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
|
|
227
|
+
case YNABErrorCode.FORBIDDEN:
|
|
225
228
|
return [
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
|
233
|
+
case YNABErrorCode.NOT_FOUND:
|
|
231
234
|
return this.getNotFoundSuggestions(context);
|
|
232
|
-
case
|
|
235
|
+
case YNABErrorCode.TOO_MANY_REQUESTS:
|
|
233
236
|
return [
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
|
241
|
+
case YNABErrorCode.INTERNAL_SERVER_ERROR:
|
|
239
242
|
return [
|
|
240
243
|
"Check YNAB's status page at https://status.youneedabudget.com",
|
|
241
|
-
|
|
242
|
-
|
|
244
|
+
"Try again in a few minutes",
|
|
245
|
+
"Contact YNAB support if the issue persists",
|
|
243
246
|
];
|
|
244
|
-
case
|
|
247
|
+
case SecurityErrorCode.VALIDATION_ERROR:
|
|
245
248
|
return [
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
279
|
-
|
|
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(
|
|
283
|
-
return [
|
|
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(
|
|
291
|
+
if (context.includes("category")) {
|
|
286
292
|
return [
|
|
287
293
|
...baseSuggestions,
|
|
288
|
-
|
|
294
|
+
"Check if the category was deleted or moved to a different group",
|
|
289
295
|
];
|
|
290
296
|
}
|
|
291
|
-
if (context.includes(
|
|
297
|
+
if (context.includes("transaction")) {
|
|
292
298
|
return [
|
|
293
299
|
...baseSuggestions,
|
|
294
|
-
|
|
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(
|
|
301
|
-
return
|
|
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(
|
|
304
|
-
return
|
|
309
|
+
if (context.includes("budget")) {
|
|
310
|
+
return "There was a problem accessing your budget data. Please try again.";
|
|
305
311
|
}
|
|
306
|
-
if (context.includes(
|
|
307
|
-
return
|
|
312
|
+
if (context.includes("account")) {
|
|
313
|
+
return "There was a problem accessing your account information. Please try again.";
|
|
308
314
|
}
|
|
309
|
-
return
|
|
315
|
+
return "Something went wrong. Please try again in a moment.";
|
|
310
316
|
}
|
|
311
317
|
getErrorMessage(code, context) {
|
|
312
318
|
switch (code) {
|
|
313
|
-
case
|
|
314
|
-
return
|
|
315
|
-
case
|
|
316
|
-
return
|
|
317
|
-
case
|
|
318
|
-
return
|
|
319
|
-
case
|
|
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
|
|
322
|
-
return
|
|
323
|
-
case
|
|
324
|
-
return
|
|
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(
|
|
331
|
-
return
|
|
336
|
+
if (context.includes("listing accounts")) {
|
|
337
|
+
return "Failed to list accounts - budget or account not found";
|
|
332
338
|
}
|
|
333
|
-
if (context.includes(
|
|
334
|
-
return
|
|
339
|
+
if (context.includes("getting account")) {
|
|
340
|
+
return "Failed to get account - budget or account not found";
|
|
335
341
|
}
|
|
336
|
-
if (context.includes(
|
|
337
|
-
|
|
342
|
+
if (context.includes("listing budgets") ||
|
|
343
|
+
context.includes("getting budget")) {
|
|
344
|
+
return "Budget not found";
|
|
338
345
|
}
|
|
339
|
-
if (context.includes(
|
|
340
|
-
|
|
346
|
+
if (context.includes("listing categories") ||
|
|
347
|
+
context.includes("getting category")) {
|
|
348
|
+
return "Budget or category not found";
|
|
341
349
|
}
|
|
342
|
-
if (context.includes(
|
|
343
|
-
|
|
350
|
+
if (context.includes("listing months") ||
|
|
351
|
+
context.includes("getting month")) {
|
|
352
|
+
return "Budget or month not found";
|
|
344
353
|
}
|
|
345
|
-
if (context.includes(
|
|
346
|
-
|
|
354
|
+
if (context.includes("listing payees") ||
|
|
355
|
+
context.includes("getting payee")) {
|
|
356
|
+
return "Budget or payee not found";
|
|
347
357
|
}
|
|
348
|
-
if (context.includes(
|
|
349
|
-
|
|
358
|
+
if (context.includes("listing transactions") ||
|
|
359
|
+
context.includes("getting transaction")) {
|
|
360
|
+
return "Budget, account, category, or transaction not found";
|
|
350
361
|
}
|
|
351
|
-
return
|
|
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(
|
|
355
|
-
return
|
|
365
|
+
if (context.includes("listing accounts")) {
|
|
366
|
+
return "Failed to list accounts";
|
|
356
367
|
}
|
|
357
|
-
if (context.includes(
|
|
358
|
-
return
|
|
368
|
+
if (context.includes("getting account")) {
|
|
369
|
+
return "Failed to get account";
|
|
359
370
|
}
|
|
360
|
-
if (context.includes(
|
|
361
|
-
return
|
|
371
|
+
if (context.includes("creating account")) {
|
|
372
|
+
return "Failed to create account";
|
|
362
373
|
}
|
|
363
|
-
if (context.includes(
|
|
364
|
-
return
|
|
374
|
+
if (context.includes("listing budgets")) {
|
|
375
|
+
return "Failed to list budgets";
|
|
365
376
|
}
|
|
366
|
-
if (context.includes(
|
|
367
|
-
return
|
|
377
|
+
if (context.includes("getting budget")) {
|
|
378
|
+
return "Failed to get budget";
|
|
368
379
|
}
|
|
369
|
-
if (context.includes(
|
|
370
|
-
return
|
|
380
|
+
if (context.includes("listing categories")) {
|
|
381
|
+
return "Failed to list categories";
|
|
371
382
|
}
|
|
372
|
-
if (context.includes(
|
|
373
|
-
return
|
|
383
|
+
if (context.includes("getting category")) {
|
|
384
|
+
return "Failed to get category";
|
|
374
385
|
}
|
|
375
|
-
if (context.includes(
|
|
376
|
-
return
|
|
386
|
+
if (context.includes("updating category")) {
|
|
387
|
+
return "Failed to update category";
|
|
377
388
|
}
|
|
378
|
-
if (context.includes(
|
|
379
|
-
return
|
|
389
|
+
if (context.includes("listing months")) {
|
|
390
|
+
return "Failed to list months";
|
|
380
391
|
}
|
|
381
|
-
if (context.includes(
|
|
382
|
-
return
|
|
392
|
+
if (context.includes("getting month")) {
|
|
393
|
+
return "Failed to get month data";
|
|
383
394
|
}
|
|
384
|
-
if (context.includes(
|
|
385
|
-
return
|
|
395
|
+
if (context.includes("listing payees")) {
|
|
396
|
+
return "Failed to list payees";
|
|
386
397
|
}
|
|
387
|
-
if (context.includes(
|
|
388
|
-
return
|
|
398
|
+
if (context.includes("getting payee")) {
|
|
399
|
+
return "Failed to get payee";
|
|
389
400
|
}
|
|
390
|
-
if (context.includes(
|
|
391
|
-
return
|
|
401
|
+
if (context.includes("listing transactions")) {
|
|
402
|
+
return "Failed to list transactions";
|
|
392
403
|
}
|
|
393
|
-
if (context.includes(
|
|
394
|
-
return
|
|
404
|
+
if (context.includes("getting transaction")) {
|
|
405
|
+
return "Failed to get transaction";
|
|
395
406
|
}
|
|
396
|
-
if (context.includes(
|
|
397
|
-
return
|
|
407
|
+
if (context.includes("creating transaction")) {
|
|
408
|
+
return "Failed to create transaction";
|
|
398
409
|
}
|
|
399
|
-
if (context.includes(
|
|
400
|
-
return
|
|
410
|
+
if (context.includes("updating transaction")) {
|
|
411
|
+
return "Failed to update transaction";
|
|
401
412
|
}
|
|
402
|
-
if (context.includes(
|
|
403
|
-
return
|
|
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 !==
|
|
419
|
+
if (!error || typeof error !== "object") {
|
|
409
420
|
return null;
|
|
410
421
|
}
|
|
411
422
|
const directStatus = error.status;
|
|
412
|
-
if (typeof directStatus ===
|
|
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 ===
|
|
429
|
+
if (response && typeof response === "object") {
|
|
417
430
|
const responseStatus = response.status;
|
|
418
|
-
if (typeof responseStatus ===
|
|
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
|
|
429
|
-
case
|
|
430
|
-
case
|
|
431
|
-
case
|
|
432
|
-
case
|
|
433
|
-
case
|
|
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 ===
|
|
453
|
+
if (error && typeof error === "object") {
|
|
441
454
|
const response = error.response;
|
|
442
|
-
if (response && typeof response ===
|
|
455
|
+
if (response && typeof response === "object") {
|
|
443
456
|
const statusText = response.statusText;
|
|
444
|
-
if (typeof statusText ===
|
|
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 !==
|
|
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
|
|
461
|
-
|
|
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 !==
|
|
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 ===
|
|
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 ===
|
|
492
|
+
if (!code && typeof name === "string") {
|
|
479
493
|
const normalized = name.toLowerCase();
|
|
480
|
-
if (normalized.includes(
|
|
481
|
-
code =
|
|
494
|
+
if (normalized.includes("unauthorized")) {
|
|
495
|
+
code = YNABErrorCode.UNAUTHORIZED;
|
|
482
496
|
}
|
|
483
|
-
else if (normalized.includes(
|
|
484
|
-
code =
|
|
497
|
+
else if (normalized.includes("forbidden")) {
|
|
498
|
+
code = YNABErrorCode.FORBIDDEN;
|
|
485
499
|
}
|
|
486
|
-
else if (normalized.includes(
|
|
487
|
-
code =
|
|
500
|
+
else if (normalized.includes("not_found")) {
|
|
501
|
+
code = YNABErrorCode.NOT_FOUND;
|
|
488
502
|
}
|
|
489
|
-
else if (normalized.includes(
|
|
490
|
-
|
|
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(
|
|
493
|
-
code =
|
|
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 ===
|
|
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 ===
|
|
528
|
+
else if (typeof error === "string") {
|
|
514
529
|
details = error;
|
|
515
530
|
}
|
|
516
531
|
else {
|
|
517
|
-
details =
|
|
532
|
+
details = "Unknown error details";
|
|
518
533
|
}
|
|
519
534
|
details = details
|
|
520
|
-
.replace(/token[s]?[:\s=]+([^\s,"']+)/gi,
|
|
521
|
-
.replace(/key[s]?[:\s=]+([^\s,"']+)/gi,
|
|
522
|
-
.replace(/password[s]?[:\s=]+([^\s,"']+)/gi,
|
|
523
|
-
.replace(/authorization[:\s=]+[^\r\n]+/gi,
|
|
524
|
-
.replace(/\bBearer\s+[A-Za-z0-9._-]+/gi,
|
|
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),
|
|
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
|
-
|
|
565
|
-
|
|
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
|
-
|
|
568
|
+
export async function withToolErrorHandling(operation, toolName, operationName, errorHandler) {
|
|
569
|
+
const eh = errorHandler ?? fallbackErrorHandler;
|
|
570
|
+
return eh.withErrorHandling(operation, `executing ${toolName} - ${operationName}`);
|
|
569
571
|
}
|