14
14
*/
15
15
16
16
import { isErrorLike } from '#isErrorLike' ;
17
- import { jsonify } from '#jsonify' ;
18
17
import { Xception } from '#prototype' ;
19
18
import { $meta , $namespace , $tags } from '#symbols' ;
20
19
21
- import type { JsonObject } from 'type-fest' ;
22
-
23
20
import type { ErrorLike } from '#isErrorLike' ;
24
21
import type { XceptionOptions } from '#prototype' ;
25
22
23
+ type Options = Omit < XceptionOptions , 'cause' > & {
24
+ factory ?: ( message : string , options : XceptionOptions ) => Xception ;
25
+ } ;
26
+
26
27
/**
27
28
* convert an error to an Xception instance with metadata merged while preserving the original error message and stack
28
29
* @param exception an exception to be converted
@@ -32,55 +33,71 @@ import type { XceptionOptions } from '#prototype';
32
33
*/
33
34
export default function xception (
34
35
exception : unknown , // string, error, Xception, { message: string, ...}
35
- options ?: Omit < XceptionOptions , 'cause' > ,
36
+ options ?: Options ,
36
37
) : Xception {
37
- const { namespace, meta = { } , tags = [ ] } = { ...options } ;
38
+ // when options.factory is provided, it's used to create Xception instances; otherwise, the default constructor is used
39
+ const factory =
40
+ options ?. factory ??
41
+ ( ( message : string , options ?: XceptionOptions ) =>
42
+ new Xception ( message , options ) ) ;
38
43
39
44
if ( isErrorLike ( exception ) ) {
40
- const error = createXceptionFromError ( exception ) ;
41
-
42
- // merge the namespace
43
- error [ $namespace ] = namespace ?? error [ $namespace ] ;
45
+ // fetch defaults if provided in options, or use properties of the original exception
46
+ const { namespace, meta, tags } = computeDefaults ( exception , options ) ;
44
47
45
- // merge the meta data
46
- error [ $meta ] = { ...error [ $meta ] , ...meta } ;
47
-
48
- // merge the tags
49
- error [ $tags ] = [ ...new Set ( [ ...error [ $tags ] , ...tags ] ) ] ;
48
+ // create a new Xception with the original error's message and computed options
49
+ const error = factory ( exception . message , { namespace, meta, tags } ) ;
50
50
51
51
// replace the name and stack from the original error
52
52
error . name = exception . name ?? error . name ;
53
53
error . stack = exception . stack ?? error . stack ;
54
54
55
55
return error ;
56
56
} else if ( typeof exception === 'string' ) {
57
- return new Xception ( exception , options ) ;
57
+ // if exception is a string, we create a new Xception with the given message and options
58
+ const { namespace, meta, tags } = { ...options } ;
59
+
60
+ return factory ( exception , { namespace, meta, tags } ) ;
58
61
}
59
62
63
+ // if exception is of unexpected type, throw a new Xception indicating the problem
60
64
throw new Xception ( 'unexpected exception type' , {
61
65
...options ,
62
66
cause : exception ,
63
67
} ) ;
64
68
}
65
69
66
70
/**
67
- * convert an error-like object to an Xception instance
68
- * @param exception an exception to be converted
69
- * @returns the transformed error
71
+ * compute defaults for namespace, meta, and tags by combining the ones present on the exception and the provided options
72
+ * @param exception the original exception-like object
73
+ * @param options additional options provided for generating the `Xception` instance
74
+ * @returns an object containing computed namespace, meta, and tags to be used for the new `Xception`
70
75
*/
71
- export function createXceptionFromError ( exception : ErrorLike ) : Xception {
72
- if ( exception instanceof Xception ) {
73
- return exception ;
74
- } else {
75
- const {
76
- name : _name ,
77
- message : _message ,
78
- stack : _stack ,
79
- ...meta
80
- } = jsonify ( exception ) as JsonObject ;
76
+ function computeDefaults (
77
+ exception : ErrorLike ,
78
+ options ?: Options ,
79
+ ) : {
80
+ namespace ?: string ;
81
+ meta : Record < string , unknown > ;
82
+ tags : string [ ] ;
83
+ } {
84
+ // if a namespace is provided in options, use it, otherwise use the original exception's namespace, if any
85
+ const namespace =
86
+ options ?. namespace ?? ( exception [ $namespace ] as string | undefined ) ;
81
87
82
- return new Xception ( exception . message , {
83
- meta,
84
- } ) ;
85
- }
88
+ // merge metadata from the original exception and options
89
+ const meta = {
90
+ ...( exception [ $meta ] as Record < string , unknown > ) ,
91
+ ...options ?. meta ,
92
+ } ;
93
+
94
+ // concatenate and deduplicate tags from both the exception and the options
95
+ const tags = [
96
+ ...new Set ( [
97
+ ...( ( exception [ $tags ] as string [ ] | undefined ) ?? [ ] ) ,
98
+ ...( options ?. tags ?? [ ] ) ,
99
+ ] ) ,
100
+ ] ;
101
+
102
+ return { namespace, meta, tags } ;
86
103
}
0 commit comments