error-message-utils 1.2.12 → 1.2.14
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/README.md +73 -14
- package/dist/error-handler/index.d.ts +7 -0
- package/dist/error-handler/index.js +1 -1
- package/dist/error-handler/utilities.d.ts +7 -0
- package/dist/error-handler/utilities.js +1 -1
- package/dist/exception/exception.d.ts +5 -4
- package/dist/exception/exception.js +1 -1
- package/dist/exception/index.d.ts +1 -1
- package/dist/exception/types.d.ts +2 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ consistent shape:
|
|
|
6
6
|
- a readable message
|
|
7
7
|
- a stable error code
|
|
8
8
|
- optional extra data
|
|
9
|
+
- an optional native error cause
|
|
9
10
|
|
|
10
11
|
Use `Exception` for most application code. Use `encodeError` and `decodeError` when you need to
|
|
11
12
|
send or receive an error code inside a plain string.
|
|
@@ -18,7 +19,8 @@ npm i -S error-message-utils
|
|
|
18
19
|
|
|
19
20
|
## Recommended Usage: Exception
|
|
20
21
|
|
|
21
|
-
`Exception` is an `Error` subclass that stores a normalized message, a code,
|
|
22
|
+
`Exception` is an `Error` subclass that stores a normalized message, a code, optional data, and an
|
|
23
|
+
optional native cause.
|
|
22
24
|
|
|
23
25
|
```typescript
|
|
24
26
|
import { Exception } from 'error-message-utils';
|
|
@@ -37,6 +39,7 @@ exception.name; // 'Exception'
|
|
|
37
39
|
exception.message; // 'Request failed'
|
|
38
40
|
exception.code; // 'REQUEST_FAILED'
|
|
39
41
|
exception.data; // null
|
|
42
|
+
exception.cause; // undefined
|
|
40
43
|
exception.toString(); // 'Request failed{(REQUEST_FAILED)}'
|
|
41
44
|
exception.toRecord();
|
|
42
45
|
// {
|
|
@@ -46,29 +49,51 @@ exception.toRecord();
|
|
|
46
49
|
// }
|
|
47
50
|
```
|
|
48
51
|
|
|
49
|
-
###
|
|
52
|
+
### Preserve an Error Cause
|
|
50
53
|
|
|
51
|
-
When
|
|
52
|
-
|
|
54
|
+
When wrapping an error, provide a stable contextual message and pass the original value through the
|
|
55
|
+
fourth `IErrorOptions` argument. This keeps the top-level message predictable while preserving the
|
|
56
|
+
original error for debugging.
|
|
53
57
|
|
|
54
58
|
```typescript
|
|
55
|
-
import { Exception } from 'error-message-utils';
|
|
59
|
+
import { Exception, extractMessage } from 'error-message-utils';
|
|
56
60
|
|
|
57
61
|
try {
|
|
58
62
|
await sendReceiptEmail();
|
|
59
|
-
} catch (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
} catch (cause) {
|
|
64
|
+
const exception = new Exception(
|
|
65
|
+
'Unable to send the receipt email.',
|
|
66
|
+
'RECEIPT_EMAIL_FAILED',
|
|
67
|
+
{ operation: 'sendReceiptEmail' },
|
|
68
|
+
{ cause },
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
exception.message; // 'Unable to send the receipt email.'
|
|
72
|
+
exception.cause === cause; // true
|
|
73
|
+
extractMessage(exception); // combines the message with a truthy cause chain
|
|
74
|
+
|
|
75
|
+
throw exception;
|
|
63
76
|
}
|
|
64
77
|
```
|
|
65
78
|
|
|
79
|
+
`IErrorOptions` is a direct alias of the native `ErrorOptions` type. This API requires the ES2022
|
|
80
|
+
TypeScript `lib` and a runtime that supports `Error` causes.
|
|
81
|
+
|
|
82
|
+
The existing `new Exception(error, code, data)` form remains supported. It normalizes the first
|
|
83
|
+
argument into the exception message but does not infer or store that argument as `cause`. Avoid
|
|
84
|
+
also appending the cause message to the top-level message because `extractMessage` already combines
|
|
85
|
+
truthy cause chains. Native `Error` semantics still retain falsy causes such as `false`, `0`, an
|
|
86
|
+
empty string, or `null`, but `extractMessage` does not append them.
|
|
87
|
+
|
|
88
|
+
`toString()` and `toRecord()` intentionally omit `cause`. A cause can contain internal or sensitive
|
|
89
|
+
details, so inspect or expose it only at an appropriate boundary.
|
|
90
|
+
|
|
66
91
|
### Extend Exception
|
|
67
92
|
|
|
68
93
|
Create small domain-specific exception classes when your app has a stable set of error codes.
|
|
69
94
|
|
|
70
95
|
```typescript
|
|
71
|
-
import { Exception } from 'error-message-utils';
|
|
96
|
+
import { Exception, type IErrorOptions } from 'error-message-utils';
|
|
72
97
|
|
|
73
98
|
const USER_ERROR_CODES = {
|
|
74
99
|
EmailTaken: 'USER_EMAIL_TAKEN',
|
|
@@ -78,8 +103,13 @@ const USER_ERROR_CODES = {
|
|
|
78
103
|
type IUserErrorCode = (typeof USER_ERROR_CODES)[keyof typeof USER_ERROR_CODES];
|
|
79
104
|
|
|
80
105
|
export class UserException extends Exception {
|
|
81
|
-
public constructor(
|
|
82
|
-
|
|
106
|
+
public constructor(
|
|
107
|
+
message: string,
|
|
108
|
+
code: IUserErrorCode,
|
|
109
|
+
data?: unknown,
|
|
110
|
+
options?: IErrorOptions,
|
|
111
|
+
) {
|
|
112
|
+
super(message, code, data, options);
|
|
83
113
|
this.name = 'UserException';
|
|
84
114
|
}
|
|
85
115
|
}
|
|
@@ -133,6 +163,29 @@ if (!result.success) {
|
|
|
133
163
|
}
|
|
134
164
|
```
|
|
135
165
|
|
|
166
|
+
### Redact Sensitive Values
|
|
167
|
+
|
|
168
|
+
`extractRedactedMessage` extracts a message using the same rules as `extractMessage`, then replaces
|
|
169
|
+
every exact sensitive value with `[redacted]`.
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
import { extractRedactedMessage } from 'error-message-utils';
|
|
173
|
+
|
|
174
|
+
const error = new Error('Request failed for token abc123.', {
|
|
175
|
+
cause: new Error('The provider rejected abc123.'),
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
extractRedactedMessage(error, ['abc123']);
|
|
179
|
+
// 'Request failed for token [redacted].; [CAUSE]: The provider rejected [redacted].'
|
|
180
|
+
|
|
181
|
+
extractRedactedMessage(error, ['different-value']);
|
|
182
|
+
// 'Request failed for token abc123.; [CAUSE]: The provider rejected abc123.'
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Matching is literal and case-sensitive. Empty values are ignored. The function does not
|
|
186
|
+
automatically identify secrets, so transformed or encoded versions must be supplied separately if
|
|
187
|
+
they also need redaction.
|
|
188
|
+
|
|
136
189
|
### Get an Error Code
|
|
137
190
|
|
|
138
191
|
Use `getErrorCode` when you only need the resolved code.
|
|
@@ -232,6 +285,7 @@ import {
|
|
|
232
285
|
decodeError,
|
|
233
286
|
encodeError,
|
|
234
287
|
extractMessage,
|
|
288
|
+
extractRedactedMessage,
|
|
235
289
|
getErrorCode,
|
|
236
290
|
hasErrorCode,
|
|
237
291
|
hasErrorCodePrefix,
|
|
@@ -240,6 +294,7 @@ import {
|
|
|
240
294
|
type IDecodedError,
|
|
241
295
|
type IErrorCode,
|
|
242
296
|
type IErrorCodeCarrier,
|
|
297
|
+
type IErrorOptions,
|
|
243
298
|
type IExceptionRecord,
|
|
244
299
|
} from 'error-message-utils';
|
|
245
300
|
```
|
|
@@ -248,13 +303,14 @@ import {
|
|
|
248
303
|
|
|
249
304
|
| Export | Description |
|
|
250
305
|
| --- | --- |
|
|
251
|
-
| `Exception` | An `Error` subclass that normalizes an unknown error into `message`, `code`, and `data`. It can also serialize itself with `toString()` or `toRecord()
|
|
306
|
+
| `Exception` | An `Error` subclass that normalizes an unknown error into `message`, `code`, and `data`, and accepts native error options such as `cause`. It can also serialize itself with `toString()` or `toRecord()`, which omit the cause. |
|
|
252
307
|
|
|
253
308
|
### Functions
|
|
254
309
|
|
|
255
310
|
| Export | Signature | Description |
|
|
256
311
|
| --- | --- | --- |
|
|
257
312
|
| `extractMessage` | `(error: unknown) => string` | Extracts the best readable message from strings, `Error` instances, nested error-like objects, `Error.cause` chains, arrays, plain objects, and Zod errors. Returns `DEFAULT_MESSAGE` when no useful message can be extracted. |
|
|
313
|
+
| `extractRedactedMessage` | `(error: unknown, sensitiveValues: readonly string[]) => string` | Extracts a message and replaces every exact, case-sensitive occurrence of each non-empty sensitive value with `[redacted]`. Returns the extracted message unchanged when no value matches. |
|
|
258
314
|
| `encodeError` | `(error: unknown, code: IErrorCode) => string` | Extracts a message from `error` and appends the wrapped code at the end of the message. |
|
|
259
315
|
| `decodeError` | `(error: unknown) => IDecodedError` | Extracts a message, resolves a code from an encoded message or code-carrying object, and returns `{ message, code, data }`. |
|
|
260
316
|
| `isEncodedError` | `(error: unknown) => boolean` | Returns `true` when `decodeError(error).code` resolves to a non-default code. |
|
|
@@ -268,6 +324,8 @@ import {
|
|
|
268
324
|
```typescript
|
|
269
325
|
type IErrorCode = string | number;
|
|
270
326
|
|
|
327
|
+
type IErrorOptions = ErrorOptions;
|
|
328
|
+
|
|
271
329
|
type IDecodedError = {
|
|
272
330
|
message: string;
|
|
273
331
|
code: IErrorCode;
|
|
@@ -288,9 +346,10 @@ type IExceptionRecord = {
|
|
|
288
346
|
| Export | Description |
|
|
289
347
|
| --- | --- |
|
|
290
348
|
| `IErrorCode` | The supported type for application error codes. |
|
|
349
|
+
| `IErrorOptions` | A direct alias of the native `ErrorOptions` type accepted by the `Exception` constructor. |
|
|
291
350
|
| `IDecodedError` | The object returned by `decodeError`. |
|
|
292
351
|
| `IErrorCodeCarrier` | A plain object shape that can provide a code to `decodeError`, `getErrorCode`, `hasErrorCode`, `hasErrorCodePrefix`, and `Exception`. |
|
|
293
|
-
| `IExceptionRecord` | The serializable object returned by `Exception.toRecord()`. |
|
|
352
|
+
| `IExceptionRecord` | The serializable object returned by `Exception.toRecord()`. It does not include `cause`. |
|
|
294
353
|
|
|
295
354
|
### Constants
|
|
296
355
|
|
|
@@ -6,6 +6,13 @@ import type { IErrorCode, IDecodedError } from '../shared/types.js';
|
|
|
6
6
|
* @returns A string containing the extracted message or the default message if extraction fails.
|
|
7
7
|
*/
|
|
8
8
|
export declare const extractMessage: (error: any) => string;
|
|
9
|
+
/**
|
|
10
|
+
* Extracts an error message and replaces exact sensitive values with a redaction marker.
|
|
11
|
+
* @param error The error to extract the message from.
|
|
12
|
+
* @param sensitiveValues The exact case-sensitive values to redact.
|
|
13
|
+
* @returns The extracted message with matching sensitive values redacted.
|
|
14
|
+
*/
|
|
15
|
+
export declare const extractRedactedMessage: (error: unknown, sensitiveValues: string[]) => string;
|
|
9
16
|
/**
|
|
10
17
|
* Encoding / Decoding
|
|
11
18
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ZodError}from"zod";import{DEFAULT_CODE,DEFAULT_MESSAGE}from"../shared/constants.js";import{wrapCode,unwrapCode}from"../utils/index.js";import{extractZodErrorMessage,getDecodedErrorCode}from"./utilities.js";const __extractMessage=(r
|
|
1
|
+
import{ZodError}from"zod";import{DEFAULT_CODE,DEFAULT_MESSAGE}from"../shared/constants.js";import{wrapCode,unwrapCode}from"../utils/index.js";import{extractZodErrorMessage,getDecodedErrorCode,redactSensitiveValues}from"./utilities.js";const __extractMessage=(e,r)=>{if("string"==typeof e&&e.length)return e;if(e instanceof ZodError)return extractZodErrorMessage(e);if(e&&"object"==typeof e){if(r.has(e))return DEFAULT_MESSAGE;r.add(e)}if(e instanceof Error&&e.message)return e.cause?`${e.message}; [CAUSE]: ${__extractMessage(e.cause,r)}`:e.message;if(e&&"object"==typeof e){if(e.message)return __extractMessage(e.message,r);if(e.msg)return __extractMessage(e.msg,r);if(e.error)return __extractMessage(e.error,r);if(e.err)return __extractMessage(e.err,r);if(e.errors)return __extractMessage(e.errors,r);if(e.errs)return __extractMessage(e.errs,r);if(e.reason)return __extractMessage(e.reason,r);if(e.reasons)return __extractMessage(e.reasons,r);if(e.issue)return __extractMessage(e.issue,r);if(e.issues)return __extractMessage(e.issues,r);if(e.data)return __extractMessage(e.data,r);try{return JSON.stringify(e)}catch{return DEFAULT_MESSAGE}}return DEFAULT_MESSAGE};export const extractMessage=e=>__extractMessage(e,new WeakSet);export const extractRedactedMessage=(e,r)=>redactSensitiveValues(extractMessage(e),r);export const encodeError=(e,r)=>`${extractMessage(e)}${wrapCode(r)}`;export const decodeError=e=>{const r=extractMessage(e),{code:t,startsAt:s}=unwrapCode(r);return{message:s>0?r.slice(0,s):r,code:getDecodedErrorCode(e,t),data:null!==e&&"object"==typeof e&&"data"in e?e.data:null}};export const isEncodedError=e=>decodeError(e).code!==DEFAULT_CODE;export const getErrorCode=e=>{const{code:r}=decodeError(e);return r!==DEFAULT_CODE?r:null};export const hasErrorCodePrefix=(e,r)=>{if(0===r.length)return!1;const t=getErrorCode(e)??e;return"string"==typeof t&&t.startsWith(r)};export const hasErrorCode=(e,r)=>null!==e&&(e===r||decodeError(e).code===r);export const isDefaultErrorMessage=(e,r=!1)=>r?e===DEFAULT_MESSAGE:"string"==typeof e&&e.includes(DEFAULT_MESSAGE);
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { ZodError } from 'zod';
|
|
2
2
|
import { IErrorCode } from '../shared/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Replaces every literal sensitive value in a message with the redaction marker.
|
|
5
|
+
* @param message The message to redact.
|
|
6
|
+
* @param sensitiveValues The exact case-sensitive values to redact.
|
|
7
|
+
* @returns The message with matching sensitive values redacted.
|
|
8
|
+
*/
|
|
9
|
+
export declare const redactSensitiveValues: (message: string, sensitiveValues: string[]) => string;
|
|
3
10
|
/**
|
|
4
11
|
* Attempts to extract a Zod error message from a ZodError instance. If unable to do so, it returns
|
|
5
12
|
* the default error message.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DEFAULT_CODE,DEFAULT_MESSAGE}from"../shared/constants.js";const
|
|
1
|
+
import{DEFAULT_CODE,DEFAULT_MESSAGE}from"../shared/constants.js";const REDACTION_REPLACEMENT="[redacted]",__collectSensitiveRanges=(e,r)=>{const s=[];return r.forEach((r=>{let t=e.indexOf(r);for(;-1!==t;)s.push({start:t,end:t+r.length}),t=e.indexOf(r,t+1)})),s},__mergeOverlappingRanges=e=>{const r=[...e].sort(((e,r)=>e.start-r.start||r.end-e.end)),s=[];return r.forEach((e=>{const r=s.at(-1);!r||e.start>=r.end?s.push({...e}):e.end>r.end&&(r.end=e.end)})),s};export const redactSensitiveValues=(e,r)=>{const s=[...new Set(r.filter((e=>e.length>0)))];if(!s.length)return e;const t=__mergeOverlappingRanges(__collectSensitiveRanges(e,s));if(!t.length)return e;const n=[];let o=0;return t.forEach((r=>{n.push(e.slice(o,r.start),"[redacted]"),o=r.end})),n.push(e.slice(o)),n.join("")};const __extractPathFromZodError=e=>e&&Array.isArray(e.issues)&&e.issues.length&&Array.isArray(e.issues[0].path)&&e.issues[0].path.length?e.issues[0].path.join("."):"Unknown path";export const extractZodErrorMessage=e=>e&&Array.isArray(e.issues)&&e.issues.length&&Array.isArray(e.issues[0].path)&&e.issues[0].message?`${e.issues[0].message} (${__extractPathFromZodError(e)})`:DEFAULT_MESSAGE;const __isErrorCodeCarrier=e=>"object"==typeof e&&null!==e&&"code"in e&&("string"==typeof e.code||"number"==typeof e.code);export const getDecodedErrorCode=(e,r)=>r!==DEFAULT_CODE?r:__isErrorCodeCarrier(e)?e.code:DEFAULT_CODE;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { IErrorCode } from '../shared/types.js';
|
|
2
|
+
import type { IErrorOptions, IExceptionRecord } from './types.js';
|
|
3
3
|
/**
|
|
4
|
-
* Error subclass that normalizes unknown errors
|
|
4
|
+
* Error subclass that normalizes unknown errors and supports native error options.
|
|
5
5
|
*/
|
|
6
6
|
export declare class Exception extends Error {
|
|
7
7
|
readonly code: IErrorCode;
|
|
@@ -11,8 +11,9 @@ export declare class Exception extends Error {
|
|
|
11
11
|
* @param error The unknown error or message to normalize.
|
|
12
12
|
* @param code The optional code that overrides any decoded code.
|
|
13
13
|
* @param data The optional data payload that overrides any decoded data.
|
|
14
|
+
* @param options The native error options, including an optional cause.
|
|
14
15
|
*/
|
|
15
|
-
constructor(error: unknown, code?: IErrorCode, data?: unknown);
|
|
16
|
+
constructor(error: unknown, code?: IErrorCode, data?: unknown, options?: IErrorOptions);
|
|
16
17
|
/**
|
|
17
18
|
* Override the default toString method to return a formatted error message with the code.
|
|
18
19
|
* @returns A string representation of the error with the code.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{decodeError,encodeError}from"../error-handler/index.js";export class Exception extends Error{code;data;constructor(e,r,t){const
|
|
1
|
+
import{decodeError,encodeError}from"../error-handler/index.js";export class Exception extends Error{code;data;constructor(e,r,t,o){const s=decodeError(e);super(s.message,o),this.name="Exception",this.code=r??s.code,this.data=void 0===t?s.data:t}toString(){return encodeError(this.message,this.code)}[Symbol.toPrimitive](e){return"string"===e||"default"===e?this.toString():null}toRecord(){return{message:this.message,code:this.code,data:this.data??null}}}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { IExceptionRecord } from './types.js';
|
|
1
|
+
export type { IErrorOptions, IExceptionRecord } from './types.js';
|
|
2
2
|
export { Exception } from './exception.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export type { IErrorCode, IDecodedError, IErrorCodeCarrier } from './shared/types.js';
|
|
2
2
|
export { DEFAULT_CODE, DEFAULT_MESSAGE } from './shared/constants.js';
|
|
3
|
-
export { extractMessage, encodeError, decodeError, isEncodedError, getErrorCode, hasErrorCodePrefix, hasErrorCode, isDefaultErrorMessage, } from './error-handler/index.js';
|
|
4
|
-
export { type IExceptionRecord, Exception } from './exception/index.js';
|
|
3
|
+
export { extractMessage, extractRedactedMessage, encodeError, decodeError, isEncodedError, getErrorCode, hasErrorCodePrefix, hasErrorCode, isDefaultErrorMessage, } from './error-handler/index.js';
|
|
4
|
+
export { type IErrorOptions, type IExceptionRecord, Exception } from './exception/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{DEFAULT_CODE,DEFAULT_MESSAGE}from"./shared/constants.js";export{extractMessage,encodeError,decodeError,isEncodedError,getErrorCode,hasErrorCodePrefix,hasErrorCode,isDefaultErrorMessage}from"./error-handler/index.js";export{Exception}from"./exception/index.js";
|
|
1
|
+
export{DEFAULT_CODE,DEFAULT_MESSAGE}from"./shared/constants.js";export{extractMessage,extractRedactedMessage,encodeError,decodeError,isEncodedError,getErrorCode,hasErrorCodePrefix,hasErrorCode,isDefaultErrorMessage}from"./error-handler/index.js";export{Exception}from"./exception/index.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "error-message-utils",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.14",
|
|
4
4
|
"description": "The error-message-utils package simplifies error management in your web applications and RESTful APIs. It ensures consistent and scalable handling of error messages, saving you time and effort. Moreover, it gives you the ability to assign custom error codes so all possible cases can be handled accordingly.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|