Skip to content

Commit cde196d

Browse files
committed
Enhance error handling by chaining error messages and updating constructors for custom error classes
1 parent 5f828af commit cde196d

File tree

6 files changed

+145
-165
lines changed

6 files changed

+145
-165
lines changed

dist/hashitaka.js

Lines changed: 52 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -27,106 +27,109 @@ https://github.com/DWTechs/Hashitaka.js
2727
import { getHashes, timingSafeEqual, createHmac, pbkdf2Sync, randomBytes } from 'node:crypto';
2828
import { isBase64, isString, isValidInteger, isIn } from '@dwtechs/checkard';
2929

30-
const HASHITAKA_PREFIX = "Hashitaka: ";
30+
const HASHITAKA_PREFIX = "Hashitaka - ";
31+
function chainMessage(message, err) {
32+
return `${message} - caused by: ${err.message}`;
33+
}
3134
class HashitakaError extends Error {
32-
constructor(message) {
33-
super(message);
34-
this.name = this.constructor.name;
35+
constructor(message, causedBy) {
36+
super(causedBy ? chainMessage(message, causedBy) : message);
37+
this.name = `${HASHITAKA_PREFIX}${this.constructor.name}`;
3538
if (Error.captureStackTrace)
3639
Error.captureStackTrace(this, this.constructor);
3740
}
3841
}
3942
class HashLengthMismatchError extends HashitakaError {
40-
constructor(message = `${HASHITAKA_PREFIX}Hashes must have the same byte length to be compared`) {
43+
constructor(message = "Hashes must have the same byte length to be compared") {
4144
super(message);
4245
this.code = "HASH_LENGTH_MISMATCH";
4346
this.statusCode = 400;
4447
}
4548
}
4649
class InvalidBase64ToDecodeError extends HashitakaError {
47-
constructor(urlSafe) {
48-
const message = `${HASHITAKA_PREFIX}Invalid base64 ${urlSafe ? 'URL-safe' : 'non URL-safe'} string to decode`;
49-
super(message);
50+
constructor(urlSafe, causedBy) {
51+
const message = `Invalid base64 ${urlSafe ? 'URL-safe' : 'non URL-safe'} string to decode`;
52+
super(message, causedBy);
5053
this.code = "INVALID_BASE64_TO_DECODE";
5154
this.statusCode = 400;
5255
}
5356
}
5457
class InvalidStringToEncodeError extends HashitakaError {
55-
constructor() {
56-
const message = `${HASHITAKA_PREFIX}Invalid string to encode in base64`;
57-
super(message);
58+
constructor(causedBy) {
59+
const message = "Invalid string to encode in base64";
60+
super(message, causedBy);
5861
this.code = "INVALID_STRING_TO_ENCODE";
5962
this.statusCode = 400;
6063
}
6164
}
6265
class InvalidStringToCompareError extends HashitakaError {
63-
constructor() {
64-
const message = `${HASHITAKA_PREFIX}Invalid string for hash comparison`;
65-
super(message);
66+
constructor(causedBy) {
67+
const message = "Invalid string for hash comparison";
68+
super(message, causedBy);
6669
this.code = "INVALID_STRING_TO_COMPARE";
6770
this.statusCode = 400;
6871
}
6972
}
7073
class InvalidHashToCompareError extends HashitakaError {
71-
constructor() {
72-
const message = `${HASHITAKA_PREFIX}Invalid hash for comparison`;
73-
super(message);
74+
constructor(causedBy) {
75+
const message = "Invalid hash for comparison";
76+
super(message, causedBy);
7477
this.code = "INVALID_HASH_TO_COMPARE";
7578
this.statusCode = 400;
7679
}
7780
}
7881
class InvalidSaltRoundsError extends HashitakaError {
79-
constructor(min, max) {
80-
const message = `${HASHITAKA_PREFIX}Invalid salt rounds, must be between ${min} and ${max}`;
81-
super(message);
82+
constructor(min, max, causedBy) {
83+
const message = `Invalid salt rounds, must be between ${min} and ${max}`;
84+
super(message, causedBy);
8285
this.code = "INVALID_SALT_ROUNDS";
8386
this.statusCode = 400;
8487
}
8588
}
8689
class InvalidKeyLengthError extends HashitakaError {
87-
constructor(min, max) {
88-
const message = `${HASHITAKA_PREFIX}Invalid key length, must be between ${min} and ${max}`;
89-
super(message);
90+
constructor(min, max, causedBy) {
91+
const message = `Invalid key length, must be between ${min} and ${max}`;
92+
super(message, causedBy);
9093
this.code = "INVALID_KEY_LENGTH";
9194
this.statusCode = 400;
9295
}
9396
}
9497
class InvalidDigestFunctionError extends HashitakaError {
95-
constructor() {
96-
const message = `${HASHITAKA_PREFIX}Invalid hash digest function`;
97-
super(message);
98+
constructor(causedBy) {
99+
const message = "Invalid hash digest function";
100+
super(message, causedBy);
98101
this.code = "INVALID_DIGEST_FUNCTION";
99102
this.statusCode = 400;
100103
}
101104
}
102105
class HmacCreationError extends HashitakaError {
103-
constructor() {
104-
const message = `${HASHITAKA_PREFIX}Failed to create HMAC hash`;
105-
super(message);
106+
constructor(causedBy) {
107+
const message = "Failed to create HMAC hash";
108+
super(message, causedBy);
106109
this.code = "HMAC_CREATION_FAILED";
107110
this.statusCode = 500;
108111
}
109112
}
110113
class Pbkdf2DerivationError extends HashitakaError {
111-
constructor() {
112-
const message = `${HASHITAKA_PREFIX}Failed to derive key using PBKDF2`;
113-
super(message);
114+
constructor(causedBy) {
115+
const message = "Failed to derive key using PBKDF2";
116+
super(message, causedBy);
114117
this.code = "PBKDF2_DERIVATION_FAILED";
115118
this.statusCode = 500;
116119
}
117120
}
118121
class InvalidStringToEncryptError extends HashitakaError {
119-
constructor() {
120-
const message = `${HASHITAKA_PREFIX}Invalid string to encrypt`;
121-
super(message);
122+
constructor(causedBy) {
123+
const message = "Invalid string to encrypt";
124+
super(message, causedBy);
122125
this.code = "INVALID_STRING_TO_ENCRYPT";
123126
this.statusCode = 400;
124127
}
125128
}
126129
class InvalidSecretToEncryptError extends HashitakaError {
127-
constructor() {
128-
const message = `${HASHITAKA_PREFIX}Invalid base64 secret for encryption`;
129-
super(message);
130+
constructor(causedBy) {
131+
const message = "Invalid base64 secret for encryption";
132+
super(message, causedBy);
130133
this.code = "INVALID_SECRET_TO_ENCRYPT";
131134
this.statusCode = 400;
132135
}
@@ -137,9 +140,7 @@ function b64Decode(str, urlSafe = true) {
137140
isBase64(str, urlSafe, true);
138141
}
139142
catch (err) {
140-
const e = new InvalidBase64ToDecodeError(urlSafe);
141-
e.cause = err;
142-
throw e;
143+
throw new InvalidBase64ToDecodeError(urlSafe, err);
143144
}
144145
if (urlSafe)
145146
str = str.replace(/-/g, "+").replace(/_/g, "/");
@@ -150,9 +151,7 @@ function b64Encode(str, urlSafe = true) {
150151
isString(str, "!0", null, true);
151152
}
152153
catch (err) {
153-
const e = new InvalidStringToEncodeError();
154-
e.cause = err;
155-
throw e;
154+
throw new InvalidStringToEncodeError(err);
156155
}
157156
let b64 = Buffer.from(str).toString("base64");
158157
if (urlSafe)
@@ -192,9 +191,7 @@ function setSaltRounds(rnds) {
192191
isValidInteger(rnds, MIN_SALT_RNDS, MAX_SALT_RNDS, true, true);
193192
}
194193
catch (err) {
195-
const e = new InvalidSaltRoundsError(MIN_SALT_RNDS, MAX_SALT_RNDS);
196-
e.cause = err;
197-
throw e;
194+
throw new InvalidSaltRoundsError(MIN_SALT_RNDS, MAX_SALT_RNDS, err);
198195
}
199196
saltRnds = rnds;
200197
return true;
@@ -207,9 +204,7 @@ function setKeyLen(len) {
207204
isValidInteger(len, MIN_KEY_LEN, MAX_KEY_LEN, true, true);
208205
}
209206
catch (err) {
210-
const e = new InvalidKeyLengthError(MIN_KEY_LEN, MAX_KEY_LEN);
211-
e.cause = err;
212-
throw e;
207+
throw new InvalidKeyLengthError(MIN_KEY_LEN, MAX_KEY_LEN, err);
213208
}
214209
keyLen = len;
215210
return true;
@@ -222,9 +217,7 @@ function setDigest(func) {
222217
isIn(digests, func, undefined, true);
223218
}
224219
catch (err) {
225-
const e = new InvalidDigestFunctionError();
226-
e.cause = err;
227-
throw e;
220+
throw new InvalidDigestFunctionError(err);
228221
}
229222
digest = func;
230223
return true;
@@ -237,9 +230,7 @@ function hash(str, secret) {
237230
return createHmac(digest, secret).update(str).digest("base64url");
238231
}
239232
catch (err) {
240-
const e = new HmacCreationError();
241-
e.cause = err;
242-
throw e;
233+
throw new HmacCreationError(err);
243234
}
244235
}
245236
function randomSalt() {
@@ -250,28 +241,22 @@ function pbkdf2(str, secret, salt) {
250241
return pbkdf2Sync(hash(str, secret), salt, saltRnds, keyLen, digest);
251242
}
252243
catch (err) {
253-
const e = new Pbkdf2DerivationError();
254-
e.cause = err;
255-
throw e;
244+
throw new Pbkdf2DerivationError(err);
256245
}
257246
}
258247
function encrypt(str, b64Secret) {
259248
try {
260249
isString(str, "!0", null, true);
261250
}
262251
catch (err) {
263-
const e = new InvalidStringToEncryptError();
264-
e.cause = err;
265-
throw e;
252+
throw new InvalidStringToEncryptError(err);
266253
}
267254
let secret;
268255
try {
269256
secret = b64Decode(b64Secret, true);
270257
}
271258
catch (err) {
272-
const e = new InvalidSecretToEncryptError();
273-
e.cause = err;
274-
throw e;
259+
throw new InvalidSecretToEncryptError(err);
275260
}
276261
const salt = randomSalt();
277262
return salt + pbkdf2(str, secret, salt).toString("hex");
@@ -282,17 +267,13 @@ function compare(str, hash, b64Secret, urlSafe = false) {
282267
isString(str, "!0", null, true);
283268
}
284269
catch (err) {
285-
const e = new InvalidStringToCompareError();
286-
e.cause = err;
287-
throw e;
270+
throw new InvalidStringToCompareError(err);
288271
}
289272
try {
290273
isString(hash, "!0", null, true);
291274
}
292275
catch (err) {
293-
const e = new InvalidHashToCompareError();
294-
e.cause = err;
295-
throw e;
276+
throw new InvalidHashToCompareError(err);
296277
}
297278
const secret = b64Decode(b64Secret, urlSafe);
298279
const salt = hash.slice(0, 32);

src/base64.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ function b64Decode(str: string, urlSafe = true): string {
1414
try {
1515
isBase64(str, urlSafe, true);
1616
} catch (err) {
17-
const e = new InvalidBase64ToDecodeError(urlSafe);
18-
e.cause = err;
19-
throw e;
17+
throw new InvalidBase64ToDecodeError(urlSafe, err);
2018
}
2119

2220
if (urlSafe)
@@ -40,9 +38,7 @@ function b64Encode(str: string, urlSafe = true): string {
4038
try {
4139
isString(str, "!0", null, true);
4240
} catch (err) {
43-
const e = new InvalidStringToEncodeError();
44-
e.cause = err;
45-
throw e;
41+
throw new InvalidStringToEncodeError(err);
4642
}
4743

4844
let b64 = Buffer.from(str).toString("base64");

src/compare.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,13 @@ function compare(
4444
try {
4545
isString(str, "!0", null, true);
4646
} catch (err) {
47-
const e = new InvalidStringToCompareError();
48-
e.cause = err;
49-
throw e;
47+
throw new InvalidStringToCompareError(err);
5048
}
5149

5250
try {
5351
isString(hash, "!0", null, true);
5452
} catch (err) {
55-
const e = new InvalidHashToCompareError();
56-
e.cause = err;
57-
throw e;
53+
throw new InvalidHashToCompareError(err);
5854
}
5955

6056
const secret = b64Decode(b64Secret, urlSafe);

0 commit comments

Comments
 (0)