forked from SmartDropLabs/smartdrop-backend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAppError.js
More file actions
84 lines (73 loc) · 3.86 KB
/
Copy pathAppError.js
File metadata and controls
84 lines (73 loc) · 3.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
'use strict';
/**
* Machine-readable application error codes (issue #253).
*
* Clients switch on `error.code`, never on the human-readable message, so
* these strings are a public API contract: renaming one is a breaking
* change for every consumer handling it. Messages can be reworded freely.
*
* Each entry carries the HTTP status it maps to, so a throw site normally
* names only the code and lets the status follow from the registry rather
* than repeating it (and risking the two drifting apart).
*
* Resource-specific codes (WEBHOOK_NOT_FOUND, AIRDROP_NOT_FOUND, …) exist
* alongside the generic ones because "not found" alone does not tell a
* client *what* was missing — the point of a machine-readable code is to
* let it react differently to a missing webhook than to a missing airdrop
* without parsing prose.
*/
const ERROR_CODES = Object.freeze({
// ── Generic ──────────────────────────────────────────────────────────
VALIDATION_ERROR: { statusCode: 400 },
UNAUTHORIZED: { statusCode: 401 },
FORBIDDEN: { statusCode: 403 },
NOT_FOUND: { statusCode: 404 },
CONFLICT: { statusCode: 409 },
PAYLOAD_TOO_LARGE: { statusCode: 413 },
UNSUPPORTED_MEDIA_TYPE: { statusCode: 415 },
TIMEOUT: { statusCode: 504 },
RATE_LIMITED: { statusCode: 429 },
INTERNAL_ERROR: { statusCode: 500 },
UPSTREAM_ERROR: { statusCode: 502 },
SERVICE_UNAVAILABLE: { statusCode: 503 },
// ── Airdrops ─────────────────────────────────────────────────────────
AIRDROP_NOT_FOUND: { statusCode: 404 },
AIRDROP_NOT_INDEXED: { statusCode: 404 },
RECIPIENT_LIMIT_EXCEEDED: { statusCode: 400 },
CSV_INVALID_ENCODING: { statusCode: 400 },
CSV_MISSING_COLUMNS: { statusCode: 400 },
CSV_MALFORMED: { statusCode: 400 },
CSV_EMPTY: { statusCode: 400 },
// ── Webhooks ─────────────────────────────────────────────────────────
WEBHOOK_NOT_FOUND: { statusCode: 404 },
WEBHOOK_LIMIT_EXCEEDED: { statusCode: 429 },
WEBHOOK_TARGET_BLOCKED: { statusCode: 422 },
INVALID_URL: { statusCode: 400 },
// ── Alerts ───────────────────────────────────────────────────────────
ALERT_NOT_FOUND: { statusCode: 404 },
// ── API keys ─────────────────────────────────────────────────────────
API_KEY_NOT_FOUND: { statusCode: 404 },
// ── Prices / upstream data sources ───────────────────────────────────
PRICE_UNAVAILABLE: { statusCode: 503 },
// ── Indexer ──────────────────────────────────────────────────────────
INDEXER_UNAVAILABLE: { statusCode: 503 },
});
class AppError extends Error {
constructor(code, message, statusCode, details = {}) {
super(message);
if (!ERROR_CODES[code]) {
throw new Error(`Unknown application error code: ${code}`);
}
this.name = 'AppError';
this.code = code;
this.statusCode = statusCode || ERROR_CODES[code].statusCode;
this.details = details;
Error.captureStackTrace?.(this, AppError);
}
}
AppError.codes = ERROR_CODES;
/** True when `code` is a registered application error code. */
AppError.isKnownCode = function isKnownCode(code) {
return Object.prototype.hasOwnProperty.call(ERROR_CODES, code);
};
module.exports = AppError;