@@ -2,13 +2,15 @@ import {
2
2
isUnknownObject ,
3
3
type UnknownObject ,
4
4
} from '@getodk/common/lib/runtime-types/shared-type-predicates.ts' ;
5
- import type { initializeForm } from '@getodk/xforms-engine' ;
5
+ import type { createInstance } from '@getodk/xforms-engine' ;
6
6
7
- interface ErrorLikeCause extends UnknownObject {
7
+ interface ErrorLike {
8
8
readonly message : string ;
9
9
readonly stack ?: string | null ;
10
10
}
11
11
12
+ interface ErrorLikeCause extends ErrorLike , UnknownObject { }
13
+
12
14
const isErrorLikeCause = ( cause : unknown ) : cause is ErrorLikeCause => {
13
15
if ( ! isUnknownObject ( cause ) ) {
14
16
return false ;
@@ -24,6 +26,13 @@ const isErrorLikeCause = (cause: unknown): cause is ErrorLikeCause => {
24
26
*/
25
27
const UNKNOWN_ERROR_MESSAGE = 'Unknown error' ;
26
28
29
+ interface FormInitializationErrorOptions {
30
+ readonly message : string ;
31
+ readonly cause ?: unknown ;
32
+ readonly unknownCauseDetail ?: string | null ;
33
+ readonly stack ?: string | null ;
34
+ }
35
+
27
36
/**
28
37
* Provides a minimal, uniform representation of most general, known form load
29
38
* failure conditions.
@@ -37,7 +46,7 @@ const UNKNOWN_ERROR_MESSAGE = 'Unknown error';
37
46
* coming refinement.
38
47
*
39
48
* We handle these broad cases, each of which may be produced by a rejected
40
- * {@link Promise}, as returned by {@link initializeForm }:
49
+ * {@link Promise}, as returned by {@link createInstance }:
41
50
*
42
51
* 1. Promise is rejected with any {@link Error}, or subclass/inheritor thereof,
43
52
* as thrown by `@getodk/xforms-engine` or `@getodk/xpath`. These are
@@ -63,7 +72,7 @@ const UNKNOWN_ERROR_MESSAGE = 'Unknown error';
63
72
* system. We will likely make some meaningful effort on this front as well,
64
73
* but we accept it will never be exhaustive. Most importantly, we cannot
65
74
* truly know what types may be thrown (and then passed through as a
66
- * {@link Promise} rejection by {@link initializeForm }).
75
+ * {@link Promise} rejection by {@link createInstance }).
67
76
*
68
77
* As such where the type of {@link cause} is...
69
78
*
@@ -86,42 +95,64 @@ const UNKNOWN_ERROR_MESSAGE = 'Unknown error';
86
95
* to `@getodk/xforms-engine`.
87
96
*/
88
97
export class FormInitializationError extends Error {
89
- readonly unknownCauseDetail : string | null ;
90
- readonly stack ?: string | undefined ;
91
-
92
- constructor ( readonly cause : unknown ) {
93
- let message : string ;
94
- let unknownCauseDetail : string | null = null ;
95
- let stack : string | null = null ;
98
+ static fromError ( cause : ErrorLike ) : FormInitializationError {
99
+ return new this ( cause ) ;
100
+ }
96
101
97
- // If `initializeForm` rejected with an error, we can derive its message and stack
102
+ static from ( cause : unknown ) : FormInitializationError {
98
103
if ( cause instanceof Error || isErrorLikeCause ( cause ) ) {
99
- message = cause . message ;
100
- stack = cause . stack ?? null ;
101
- } else if ( isUnknownObject ( cause ) && typeof cause . message === 'string' ) {
102
- message = cause . message ;
103
- } else if ( typeof cause === 'string' ) {
104
- message = cause ;
105
- } else {
106
- message = 'Unknown error' ;
104
+ return this . fromError ( cause ) ;
105
+ }
107
106
108
- try {
109
- unknownCauseDetail = JSON . stringify ( cause , null , 2 ) ;
110
- } catch {
111
- // Ignore JSON serialization error
112
- }
107
+ if ( isUnknownObject ( cause ) && typeof cause . message === 'string' ) {
108
+ return new this ( {
109
+ message : cause . message ,
110
+ cause ,
111
+ } ) ;
113
112
}
114
113
114
+ if ( typeof cause === 'string' ) {
115
+ return new this ( {
116
+ message : cause ,
117
+ cause,
118
+ } ) ;
119
+ }
120
+
121
+ let unknownCauseDetail : string | null = null ;
122
+
123
+ try {
124
+ unknownCauseDetail = JSON . stringify ( cause , null , 2 ) ;
125
+ } catch {
126
+ // Ignore JSON serialization error
127
+ }
128
+
129
+ return new this ( {
130
+ message : 'Unknown error' ,
131
+ cause,
132
+ unknownCauseDetail,
133
+ } ) ;
134
+ }
135
+
136
+ readonly cause : unknown ;
137
+ readonly unknownCauseDetail : string | null ;
138
+ readonly stack ?: string | undefined ;
139
+
140
+ private constructor ( options : FormInitializationErrorOptions ) {
141
+ let message = options . message ;
142
+
115
143
// TODO: this occurs when Solid's production build detects a "potential
116
144
// infinite loop" (i.e. either a form cycle, or potentially a serious bug in
117
145
// the engine!).
118
146
if ( message === '' ) {
119
147
message = 'Unknown error' ;
120
148
}
121
149
150
+ const { cause, unknownCauseDetail, stack } = options ;
151
+
122
152
super ( message , { cause } ) ;
123
153
124
- this . unknownCauseDetail = unknownCauseDetail ;
154
+ this . cause = cause ;
155
+ this . unknownCauseDetail = unknownCauseDetail ?? null ;
125
156
126
157
if ( stack != null ) {
127
158
this . stack = stack ;
0 commit comments