@smbcheeky/error-object-from-payload 1.1.5

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 SMBCheeky
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,188 @@
1
+ [![License](https://img.shields.io/npm/l/@smbcheeky/error-object)](LICENSE_FILE)
2
+ [![deno.bundlejs.com](https://deno.bundlejs.com/badge?q=@smbcheeky/error-object-from-payload&treeshake=[*])](https://deno.bundlejs.com/?q=@smbcheeky/error-object-from-payload&treeshake=[*])
3
+ [![npm downloads](https://img.shields.io/npm/dm/@smbcheeky/error-object-from-payload)](https://www.npmjs.com/package/@smbcheeky/error-object-from-payload)
4
+ [![GitHub last commit](https://img.shields.io/github/last-commit/smbcheeky/error-object-from-payload)](https://github.com/smbcheeky/error-object-from-payload)
5
+ [![GitHub stars](https://img.shields.io/github/stars/smbcheeky/error-object-from-payload)](https://img.shields.io/github/stars/smbcheeky/error-object-from-payload)
6
+
7
+ ## TL;DR
8
+
9
+ - Install the package `npm install @smbcheeky/error-object @smbcheeky/error-object-from-payload`
10
+ - Write `fromPayload(<pick an api response with an error>).force.verboseLog('LOG')` and use the info provided to
11
+ map your error
12
+ - Switch the .force with .error, and now you have an error object
13
+ - :tada:
14
+ - oh... and check the [playground](https://github.com/SMBCheeky/error-object-from-payload/blob/main/playground/index.ts)
15
+ file
16
+
17
+ ## Installation
18
+
19
+ `npm install @smbcheeky/error-object @smbcheeky/error-object-from-payload`
20
+
21
+ `yarn add @smbcheeky/error-object @smbcheeky/error-object-from-payload`
22
+
23
+ ## Description
24
+
25
+ The ErrorObject class is made to extend `Error` enabling checks like `errorObject instanceof Error` or
26
+ `errorObject instanceof ErrorObject`. The `ErrorObject` class is backwards compatible with `Error`.
27
+
28
+ This functionality was extracted from the [error-object](https://github.com/SMBCheeky/error-object) package (at v1.1.5)
29
+ and renamed to `error-object-from-payload` to simplify the main package and its README. This package is not a
30
+ replacement for the `error-object` package, it is just a helper to simplify the process of creating errors from any
31
+ payload by providing step-by-step debugging logs for the entire process.
32
+
33
+ ## Usage
34
+
35
+ Use `fromPayload(<anything>)` to create errors from any input:
36
+
37
+ - you can pass an object or a caught error to it, and it will try its best to create an error from it
38
+ - `fromPayload(<anything>)` returns an object with two properties: `.error` and `.force`
39
+ - `.error` represents the error, if it can be created, otherwise it is `undefined`
40
+ - `.force` represents the error, if it can be created, otherwise it is going to return a `ErrorObject.fallback()`
41
+ error
42
+
43
+ The processing of the ErrorObject is done in a few steps, based on the `ErrorObjectBuildOptions`:
44
+
45
+ - first the initial object is checked via the options `checkInputObjectForValues` and `checkInputObjectForTypes` and
46
+ `checkInputObjectForKeys`
47
+ - then the objects checks for an object array at `pathToErrors`, which could be an array of errors
48
+ - if an error array is found, the process will consider all other paths relative to the objects in the error array found
49
+ - if an error array is not found, the process will consider all other paths absolute to the initial objectpassed to
50
+ `fromPayload()`
51
+ - the `pathToCode`, `pathToNumberCode`, `pathToMessage`, `pathToDetails` and `pathToDomain` options are used to map
52
+ values to their associated field, if found
53
+ - for all fields other than `numberCode`, if a value is found and is a string, it is saved as is, but if it is an array
54
+ or an object it will be JSON.stringify'ed and saved as a string
55
+ - for `numberCode`, if a value is found and it is a number different than `NaN`, it is saved
56
+ - the `transform` function is used to transform the found values by the parsing process into the error object
57
+ - the transform function has access to all pre-transformation values and also the initial object (object inside the
58
+ errors array or initial object)
59
+ - everything gets processed into a list of `ErrorSummary | ErrorObjectErrorResult` array
60
+ - it contains everything, from error strings custom-made to be as distinct and easy to read as possible, to self
61
+ documenting summaries of what values are found, at which path, if an errors object was found, etc.
62
+ - the count of the list is meant to be an indication of how many input objects were found and processed, as each of them
63
+ should become an error object
64
+ - in the last step of the process, the list is filtered down and a single error object is created, with everything baked
65
+ in
66
+ - think detailed `processingErrors` which includes the summaries and the errors that were triggered during the process,
67
+ the `raw` object that was used as in input for the fromPayload() call and the `nextErrors` array which allows for
68
+ all errors to be saved on one single error object for later use
69
+
70
+ ## Usage & Examples
71
+
72
+ For a guide on how to use the library, please check the first detailed example in
73
+ the [playground](https://github.com/SMBCheeky/error-object/blob/main/playground/index.ts) file.
74
+
75
+ ```typescript
76
+ new ErrorObject({ code: '', message: 'Something went wrong.', domain: 'auth' }).debugLog('LOG');
77
+
78
+ fromPayload({ code: '', message: 'Something went wrong', domain: 'auth' })?.force?.debugLog('LOG');
79
+
80
+ // Example 12 output:
81
+ //
82
+ // [LOG] Something went wrong. [auth]
83
+ // {
84
+ // "code": "",
85
+ // "message": "Something went wrong.",
86
+ // "domain": "auth"
87
+ // }
88
+ //
89
+ // [LOG] Something went wrong [auth]
90
+ // {
91
+ // "code": "",
92
+ // "message": "Something went wrong",
93
+ // "domain": "auth"
94
+ // }
95
+ ```
96
+
97
+ ```typescript
98
+ const response = {
99
+ statusCode: 400,
100
+ headers: {
101
+ 'Content-Type': 'application/json',
102
+ },
103
+ body: '{"error":"Invalid input data","code":400}',
104
+ };
105
+
106
+ fromPayload(JSON.parse(response?.body), {
107
+ pathToNumberCode: ['code'],
108
+ pathToMessage: ['error'],
109
+ }).force?.debugLog('LOG');
110
+
111
+ // Example 6 output:
112
+ //
113
+ // [LOG] Invalid input data [400]
114
+ // {
115
+ // "code": "400",
116
+ // "numberCode": 400,
117
+ // "message": "Invalid input data"
118
+ // }
119
+ ```
120
+
121
+ ```typescript
122
+ /*
123
+ * You could have a file called `errors.ts` in each of your modules/folders and
124
+ * define a function like `createAuthError2()` that returns an error object with
125
+ * the correct message and domain.
126
+ */
127
+ const AuthMessageResolver = (
128
+ beforeTransform: ErrorObjectTransformState): ErrorObjectTransformState => {
129
+ // Quick tip: Make all messages slightly different, to make it easy
130
+ // to find the right one when debugging, even in production
131
+ let message: string | undefined;
132
+ switch (beforeTransform.code) {
133
+ case 'generic':
134
+ message = 'Something went wrong';
135
+ break;
136
+ case 'generic-again':
137
+ message = 'Something went wrong. Please try again.';
138
+ break;
139
+ case 'generic-network':
140
+ message = 'Something went wrong. Please check your internet connection and try again.';
141
+ break;
142
+ default:
143
+ message = 'Something went wrong.';
144
+ }
145
+ return { ...beforeTransform, message };
146
+ };
147
+
148
+ const createAuthError2 = (code: string) => {
149
+ return fromPayload({ code, domain: 'auth', }, { transform: AuthMessageResolver, });
150
+ };
151
+
152
+
153
+ createAuthError2('generic')?.error?.log('1');
154
+ createAuthError2('generic-again')?.error?.log('2');
155
+ createAuthError2('generic-network')?.error?.log('3');
156
+ createAuthError2('invalid-code')?.error?.log('4');
157
+
158
+ // Example 2 output:
159
+ //
160
+ // [1] Something went wrong [auth/generic]
161
+ // [2] Something went wrong. Please try again. [auth/generic-again]
162
+ // [3] Something went wrong. Please check your internet connection and try again. [auth/generic-network]
163
+ // [4] Something went wrong. [auth/invalid-code]
164
+ ```
165
+
166
+ ## FAQ
167
+
168
+ ### How do I use paths? Are they absolute?
169
+
170
+ To support inputs containing arrays of errors as well as single errors, all paths are treated initially as absolute (
171
+ from the
172
+ input root), but if an array of errors is detected, it will consider each element found the new root input object. Devs
173
+ have a
174
+ choice: set the "pathToErrors" option as empty, and then map only the first error (highly not recommended), or adjust
175
+ the paths to be relative to the objects inside the detected errors array.
176
+
177
+ ### How do I use paths? I sometimes get the error code in an `error` object, and sometimes in the root object...
178
+
179
+ You can use `pathToCode: addPrefixPathVariants('error', ['code']),` or `pathToCode: ['error.code']`
180
+
181
+ ### How do I use paths? Can I get the raw contents of a path and process it later?
182
+
183
+ Yes, you can. You can use paths like `error.details.0` to get a raw value, and then process it later using the
184
+ `transform` option.
185
+ If the value is not a string, it will be converted to a string using `JSON.stringify` to ensure everything works as
186
+ intended.
187
+ Remember, for an ErrorObject to be created, it needs at least a code and a message, and both are required to be string
188
+ values.
@@ -0,0 +1,134 @@
1
+ import { ErrorObject } from '@smbcheeky/error-object';
2
+
3
+ /**
4
+ * For customizing the paths, please make sure to use the {@link addPrefixPathVariants} function to also add the `error.` prefixed paths to the pathTo... arrays used in the {@link ErrorObjectBuildOptions} type.
5
+ */
6
+ declare const addPrefixPathVariants: (prefix: string | string[], array: string[]) => string[];
7
+ /**
8
+ * The {@link DEFAULT_BUILD_OPTIONS} is a set of default options that can be used to customize the behavior of the {@link fromPayload()} method.
9
+ * It contains common paths and a transform function that automatically converts the error number code, if it exists, to a string and sets it as the error code.
10
+ */
11
+ declare const DEFAULT_BUILD_OPTIONS: ErrorObjectBuildOptions;
12
+ /**
13
+ * The {@link ErrorObjectTransformState} type contains the state of the error object before and after the transformation.
14
+ */
15
+ type ErrorObjectTransformState = {
16
+ code?: string | undefined;
17
+ numberCode?: number | undefined;
18
+ message?: string | undefined;
19
+ details?: string | undefined;
20
+ domain?: string | undefined;
21
+ };
22
+ /**
23
+ * The {@link ErrorObjectBuildOptions} type contains all the options that can be used to customize the behavior of the {@link fromPayload()} method.
24
+ * Options are self-explanatory and the code behind them is kept similar and very straightforward, by design.
25
+ */
26
+ type ErrorObjectBuildOptions = {
27
+ /**
28
+ * The {@link checkInputObjectForValues} option allows you to check if the input object contains specific values.
29
+ */
30
+ checkInputObjectForValues?: {
31
+ [key: string]: {
32
+ value: string | number | boolean | null;
33
+ exists: boolean;
34
+ };
35
+ };
36
+ /**
37
+ * The {@link checkInputObjectForTypes} option allows you to check if the input object contains specific types.
38
+ */
39
+ checkInputObjectForTypes?: {
40
+ [key: string]: {
41
+ type: 'string' | 'number' | 'boolean' | 'object';
42
+ valueIsArray?: boolean;
43
+ exists: boolean;
44
+ };
45
+ };
46
+ /**
47
+ * The {@link checkInputObjectForKeys} option allows you to check if the input object contains specific keys.
48
+ */
49
+ checkInputObjectForKeys?: {
50
+ [key: string]: {
51
+ exists: boolean;
52
+ };
53
+ };
54
+ /**
55
+ * All paths should be absolute, from the root of the input object, unless an array of errors is found.
56
+ * When an array of errors is found, the paths are considered relative to the objects found in the errors array.
57
+ * "[" and "]" chars are not allowed. Use paths like "errors.0.code" instead of "errors[0].code".
58
+ */
59
+ pathToErrors: string[];
60
+ /**
61
+ * Path to the error code. This is the main error code that is used to identify the error.
62
+ */
63
+ pathToCode: string[];
64
+ /**
65
+ * Path to the error number code. This is an optional property that can be used to provide a numeric error code.
66
+ * The default options will automatically convert the number code to a string and set it as the error code + keep the original number code.
67
+ */
68
+ pathToNumberCode: string[];
69
+ /**
70
+ * Path to the error message. This is the main error message that is used to describe the error.
71
+ */
72
+ pathToMessage: string[];
73
+ /**
74
+ * Path to the error details. This is an optional property that can be used to provide additional details about the error.
75
+ * A common pattern is to use this to keep extra information about the error.
76
+ */
77
+ pathToDetails: string[];
78
+ /**
79
+ * Path to the error domain. This is an optional property that can be used to provide a domain for the error.
80
+ */
81
+ pathToDomain: string[];
82
+ /**
83
+ * The transform function is used to transform the properties found during the process of building the error object.
84
+ * This is useful for transforming a the message based on the error code, the domain based on the error code, etc. allowing
85
+ * the developer to customize the final error object created.
86
+ */
87
+ transform?: (beforeTransform: ErrorObjectTransformState, inputObject: any) => ErrorObjectTransformState;
88
+ };
89
+ type PathValueAndTransform<V> = {
90
+ path: string | undefined;
91
+ beforeTransform: V | undefined;
92
+ value: V | undefined;
93
+ };
94
+ /**
95
+ * The {@link ErrorSummary} helps with debugging and troubleshooting the error object building process.
96
+ * It contains the input object and the properties found during the process of building the error object.
97
+ */
98
+ type ErrorSummary = {
99
+ didDetectErrorsArray?: boolean;
100
+ input: NonNullable<Record<string, any>>;
101
+ path?: string;
102
+ value: {
103
+ code?: PathValueAndTransform<string>;
104
+ numberCode?: PathValueAndTransform<number>;
105
+ message?: PathValueAndTransform<string>;
106
+ details?: PathValueAndTransform<string>;
107
+ domain?: PathValueAndTransform<string>;
108
+ };
109
+ };
110
+ /**
111
+ * The {@link ErrorObjectErrorResult} type contains all the possible error results that can be returned by the {@link fromPayload()} method.
112
+ * The error results are used to identify the type of error that occurred during the process of building the error object.
113
+ */
114
+ type ErrorObjectErrorResult = 'isNullish' | 'isNotAnObject' | 'checkIsNullish' | 'checkIsNotAnObject' | 'isNotAnArray' | 'checkInputObjectForValuesIsNotAnObject' | 'checkInputObjectForValuesFailed' | 'checkInputObjectForTypesIsNotAnObject' | 'checkInputObjectForTypesFailed' | 'checkInputObjectForTypesValueIsArrayFailed' | 'checkInputObjectForKeysIsNotAnObject' | 'checkInputObjectForKeysFailed' | 'pathToErrorsIsNotAnArray' | 'pathToErrorsValuesAreNotStrings' | 'pathToCodeIsInvalid' | 'pathToCodeIsNotAnArray' | 'pathToCodeValuesAreNotStrings' | 'pathToNumberCodeIsInvalid' | 'pathToNumberCodeIsNotAnArray' | 'pathToNumberCodeValuesAreNotStrings' | 'pathToMessageIsInvalid' | 'pathToMessageIsNotAnArray' | 'pathToMessageValuesAreNotStrings' | 'pathToDetailsIsInvalid' | 'pathToDetailsIsNotAnArray' | 'pathToDetailsValuesAreNotStrings' | 'pathToDomainIsInvalid' | 'pathToDomainIsNotAnArray' | 'pathToDomainValuesAreNotStrings' | 'transformIsNotAFunction' | 'transformResultIsNotAValidObject' | 'transformCodeResultIsNotString' | 'transformNumberCodeResultIsNotNumber' | 'transformNumberCodeResultIsNaN' | 'transformMessageResultIsNotString' | 'transformDetailsResultIsNotString' | 'transformDomainResultIsNotString' | 'buildSummaryIsNullish' | 'buildSummaryIsNotAnObject' | 'generalBuildSummariesFromObjectError' | 'generalBuildSummaryFromObjectError' | 'generalCheckInputObjectForValuesError' | 'unknownCodeOrMessage' | 'invalidSummary';
115
+ /**
116
+ * The {@link ErrorObjectProcessingError} type contains the error result and the error summary.
117
+ * It provides all the information needed to identify and troubleshoot the error that occurred during the process of building the error object.
118
+ */
119
+ type ErrorObjectProcessingError = {
120
+ errorCode: ErrorObjectErrorResult;
121
+ summary?: ErrorSummary;
122
+ };
123
+
124
+ /**
125
+ * The {@link fromPayload()} method is an alternative way to create an {@link ErrorObject} from anything resembling an error.
126
+ * It contains options for customizing how the input is processed and how the error is built.
127
+ * Check out the {@link ErrorObjectBuildOptions} type for more details.
128
+ */
129
+ declare const fromPayload: (value: any, withOptions?: Partial<ErrorObjectBuildOptions>) => {
130
+ error?: ErrorObject;
131
+ force: ErrorObject;
132
+ };
133
+
134
+ export { DEFAULT_BUILD_OPTIONS, ErrorObjectBuildOptions, ErrorObjectErrorResult, ErrorObjectProcessingError, ErrorObjectTransformState, ErrorSummary, addPrefixPathVariants, fromPayload };