1+ /*
2+ Copyright 2023 The Matrix.org Foundation C.I.C.
3+
4+ Licensed under the Apache License, Version 2.0 (the "License");
5+ you may not use this file except in compliance with the License.
6+ You may obtain a copy of the License at
7+
8+ http://www.apache.org/licenses/LICENSE-2.0
9+
10+ Unless required by applicable law or agreed to in writing, software
11+ distributed under the License is distributed on an "AS IS" BASIS,
12+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ See the License for the specific language governing permissions and
14+ limitations under the License.
15+ */
16+
17+ export const ErrorValue = Symbol ( "ErrorBoundary:Error" ) ;
18+
19+ export class ErrorBoundary {
20+ private _error ?: Error ;
21+
22+ constructor ( private readonly errorCallback : ( Error ) => void ) { }
23+
24+ /**
25+ * Executes callback() and then runs errorCallback() on error.
26+ * This will never throw but instead return `errorValue` if an error occured.
27+ */
28+ try < T > ( callback : ( ) => T ) : T | typeof ErrorValue ;
29+ try < T > ( callback : ( ) => Promise < T > ) : Promise < T | typeof ErrorValue > | typeof ErrorValue {
30+ try {
31+ let result : T | Promise < T | typeof ErrorValue > = callback ( ) ;
32+ if ( result instanceof Promise ) {
33+ result = result . catch ( err => {
34+ this . _error = err ;
35+ this . errorCallback ( err ) ;
36+ return ErrorValue ;
37+ } ) ;
38+ }
39+ return result ;
40+ } catch ( err ) {
41+ this . _error = err ;
42+ this . errorCallback ( err ) ;
43+ return ErrorValue ;
44+ }
45+ }
46+
47+ get error ( ) : Error | undefined {
48+ return this . _error ;
49+ }
50+ }
51+
52+ export function tests ( ) {
53+ return {
54+ "catches sync error" : assert => {
55+ let emitted = false ;
56+ const boundary = new ErrorBoundary ( ( ) => emitted = true ) ;
57+ const result = boundary . try ( ( ) => {
58+ throw new Error ( "fail!" ) ;
59+ } ) ;
60+ assert ( emitted ) ;
61+ assert . strictEqual ( result , ErrorValue ) ;
62+ } ,
63+ "return value of callback is forwarded" : assert => {
64+ let emitted = false ;
65+ const boundary = new ErrorBoundary ( ( ) => emitted = true ) ;
66+ const result = boundary . try ( ( ) => {
67+ return "hello" ;
68+ } ) ;
69+ assert ( ! emitted ) ;
70+ assert . strictEqual ( result , "hello" ) ;
71+ } ,
72+ "catches async error" : async assert => {
73+ let emitted = false ;
74+ const boundary = new ErrorBoundary ( ( ) => emitted = true ) ;
75+ const result = await boundary . try ( async ( ) => {
76+ throw new Error ( "fail!" ) ;
77+ } ) ;
78+ assert ( emitted ) ;
79+ assert . strictEqual ( result , ErrorValue ) ;
80+ }
81+ }
82+ }
0 commit comments