error-message-utils 1.2.11 → 1.2.13

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 CHANGED
@@ -133,6 +133,29 @@ if (!result.success) {
133
133
  }
134
134
  ```
135
135
 
136
+ ### Redact Sensitive Values
137
+
138
+ `extractRedactedMessage` extracts a message using the same rules as `extractMessage`, then replaces
139
+ every exact sensitive value with `[redacted]`.
140
+
141
+ ```typescript
142
+ import { extractRedactedMessage } from 'error-message-utils';
143
+
144
+ const error = new Error('Request failed for token abc123.', {
145
+ cause: new Error('The provider rejected abc123.'),
146
+ });
147
+
148
+ extractRedactedMessage(error, ['abc123']);
149
+ // 'Request failed for token [redacted].; [CAUSE]: The provider rejected [redacted].'
150
+
151
+ extractRedactedMessage(error, ['different-value']);
152
+ // 'Request failed for token abc123.; [CAUSE]: The provider rejected abc123.'
153
+ ```
154
+
155
+ Matching is literal and case-sensitive. Empty values are ignored. The function does not
156
+ automatically identify secrets, so transformed or encoded versions must be supplied separately if
157
+ they also need redaction.
158
+
136
159
  ### Get an Error Code
137
160
 
138
161
  Use `getErrorCode` when you only need the resolved code.
@@ -157,6 +180,19 @@ hasErrorCode(exception, 'ACCESS_DENIED'); // true
157
180
  hasErrorCode(exception, 'PAYMENT_FAILED'); // false
158
181
  ```
159
182
 
183
+ Use `hasErrorCodePrefix` when related string codes share a prefix. Empty prefixes and numeric codes
184
+ do not match.
185
+
186
+ ```typescript
187
+ import { Exception, hasErrorCodePrefix } from 'error-message-utils';
188
+
189
+ const exception = new Exception('User not found', 'USER_NOT_FOUND');
190
+
191
+ hasErrorCodePrefix(exception, 'USER_'); // true
192
+ hasErrorCodePrefix('USER_NOT_FOUND', 'USER_'); // true
193
+ hasErrorCodePrefix(exception, 'PAYMENT_'); // false
194
+ ```
195
+
160
196
  ### Encode and Decode Plain Strings
161
197
 
162
198
  `encodeError` and `decodeError` are lower-level helpers for systems that can only pass string
@@ -219,8 +255,10 @@ import {
219
255
  decodeError,
220
256
  encodeError,
221
257
  extractMessage,
258
+ extractRedactedMessage,
222
259
  getErrorCode,
223
260
  hasErrorCode,
261
+ hasErrorCodePrefix,
224
262
  isDefaultErrorMessage,
225
263
  isEncodedError,
226
264
  type IDecodedError,
@@ -241,11 +279,13 @@ import {
241
279
  | Export | Signature | Description |
242
280
  | --- | --- | --- |
243
281
  | `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. |
282
+ | `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. |
244
283
  | `encodeError` | `(error: unknown, code: IErrorCode) => string` | Extracts a message from `error` and appends the wrapped code at the end of the message. |
245
284
  | `decodeError` | `(error: unknown) => IDecodedError` | Extracts a message, resolves a code from an encoded message or code-carrying object, and returns `{ message, code, data }`. |
246
285
  | `isEncodedError` | `(error: unknown) => boolean` | Returns `true` when `decodeError(error).code` resolves to a non-default code. |
247
286
  | `getErrorCode` | `(error: unknown) => IErrorCode \| null` | Returns the resolved non-default code, or `null` when no non-default code is found. |
248
287
  | `hasErrorCode` | `(error: unknown, code: IErrorCode) => boolean` | Checks whether an error resolves to the provided code. Raw code values are compared with strict equality. |
288
+ | `hasErrorCodePrefix` | `(error: unknown, prefix: string) => boolean` | Checks whether an error resolves to a string code that starts with the provided non-empty prefix. Raw string codes are supported; numeric codes do not match. |
249
289
  | `isDefaultErrorMessage` | `(value: string, fullMatch?: boolean) => boolean` | Checks whether a string contains `DEFAULT_MESSAGE`. Pass `true` as the second argument to require an exact match. |
250
290
 
251
291
  ### Types
@@ -274,7 +314,7 @@ type IExceptionRecord = {
274
314
  | --- | --- |
275
315
  | `IErrorCode` | The supported type for application error codes. |
276
316
  | `IDecodedError` | The object returned by `decodeError`. |
277
- | `IErrorCodeCarrier` | A plain object shape that can provide a code to `decodeError`, `getErrorCode`, `hasErrorCode`, and `Exception`. |
317
+ | `IErrorCodeCarrier` | A plain object shape that can provide a code to `decodeError`, `getErrorCode`, `hasErrorCode`, `hasErrorCodePrefix`, and `Exception`. |
278
318
  | `IExceptionRecord` | The serializable object returned by `Exception.toRecord()`. |
279
319
 
280
320
  ### Constants
@@ -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
  */
@@ -38,6 +45,13 @@ export declare const isEncodedError: (error: any) => boolean;
38
45
  * @returns The error code or null if it matches the default code.
39
46
  */
40
47
  export declare const getErrorCode: (error: any) => IErrorCode | null;
48
+ /**
49
+ * Checks if the given error has a string code that starts with the specified prefix.
50
+ * @param error The error to be checked, can be of any type.
51
+ * @param prefix The non-empty prefix to check against.
52
+ * @returns A boolean indicating whether the error code starts with the specified prefix.
53
+ */
54
+ export declare const hasErrorCodePrefix: (error: unknown, prefix: string) => boolean;
41
55
  /**
42
56
  * Checks if the given error matches the specified error code.
43
57
  * @param error The error to be checked, can be of any type.
@@ -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,e)=>{if("string"==typeof r&&r.length)return r;if(r instanceof ZodError)return extractZodErrorMessage(r);if(r&&"object"==typeof r){if(e.has(r))return DEFAULT_MESSAGE;e.add(r)}if(r instanceof Error&&r.message)return r.cause?`${r.message}; [CAUSE]: ${__extractMessage(r.cause,e)}`:r.message;if(r&&"object"==typeof r){if(r.message)return __extractMessage(r.message,e);if(r.msg)return __extractMessage(r.msg,e);if(r.error)return __extractMessage(r.error,e);if(r.err)return __extractMessage(r.err,e);if(r.errors)return __extractMessage(r.errors,e);if(r.errs)return __extractMessage(r.errs,e);if(r.reason)return __extractMessage(r.reason,e);if(r.reasons)return __extractMessage(r.reasons,e);if(r.issue)return __extractMessage(r.issue,e);if(r.issues)return __extractMessage(r.issues,e);if(r.data)return __extractMessage(r.data,e);try{return JSON.stringify(r)}catch(e){console.error("Error during extractMessage:"),console.error("Original Error: ",r),console.error("JSON.stringify Error:",e)}}return DEFAULT_MESSAGE};export const extractMessage=r=>__extractMessage(r,new WeakSet);export const encodeError=(r,e)=>`${extractMessage(r)}${wrapCode(e)}`;export const decodeError=r=>{const e=extractMessage(r),{code:t,startsAt:s}=unwrapCode(e);return{message:s>0?e.slice(0,s):e,code:getDecodedErrorCode(r,t),data:null!==r&&"object"==typeof r&&"data"in r?r.data:null}};export const isEncodedError=r=>decodeError(r).code!==DEFAULT_CODE;export const getErrorCode=r=>{const{code:e}=decodeError(r);return e!==DEFAULT_CODE?e:null};export const hasErrorCode=(r,e)=>null!==r&&(r===e||decodeError(r).code===e);export const isDefaultErrorMessage=(r,e=!1)=>e?r===DEFAULT_MESSAGE:"string"==typeof r&&r.includes(DEFAULT_MESSAGE);
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 __extractPathFromZodError=r=>r&&Array.isArray(r.issues)&&r.issues.length&&Array.isArray(r.issues[0].path)&&r.issues[0].path.length?r.issues[0].path.join("."):"Unknown path";export const extractZodErrorMessage=r=>r&&Array.isArray(r.issues)&&r.issues.length&&Array.isArray(r.issues[0].path)&&r.issues[0].message?`${r.issues[0].message} (${__extractPathFromZodError(r)})`:DEFAULT_MESSAGE;const __isErrorCodeCarrier=r=>"object"==typeof r&&null!==r&&"code"in r&&("string"==typeof r.code||"number"==typeof r.code);export const getDecodedErrorCode=(r,s)=>s!==DEFAULT_CODE?s:__isErrorCodeCarrier(r)?r.code:DEFAULT_CODE;
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;
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, hasErrorCode, isDefaultErrorMessage, } from './error-handler/index.js';
3
+ export { extractMessage, extractRedactedMessage, encodeError, decodeError, isEncodedError, getErrorCode, hasErrorCodePrefix, hasErrorCode, isDefaultErrorMessage, } from './error-handler/index.js';
4
4
  export { 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,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.11",
3
+ "version": "1.2.13",
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",