1
+ import * as nexus from 'nexus-rpc' ;
2
+ import Long from 'long' ;
3
+ import type { temporal } from '@temporalio/proto' ;
1
4
import {
2
5
ActivityFailure ,
3
6
ApplicationFailure ,
@@ -8,16 +11,43 @@ import {
8
11
encodeRetryState ,
9
12
encodeTimeoutType ,
10
13
FAILURE_SOURCE ,
14
+ NexusOperationFailure ,
11
15
ProtoFailure ,
12
16
ServerFailure ,
13
17
TemporalFailure ,
14
18
TerminatedFailure ,
15
19
TimeoutFailure ,
16
20
} from '../failure' ;
21
+ import { makeProtoEnumConverters } from '../internal-workflow' ;
17
22
import { isError } from '../type-helpers' ;
18
23
import { msOptionalToTs } from '../time' ;
19
24
import { arrayFromPayloads , fromPayloadsAtIndex , PayloadConverter , toPayloads } from './payload-converter' ;
20
25
26
+ // Can't import enums into the workflow sandbox, use this helper type and enum converter instead.
27
+ const NexusHandlerErrorRetryBehavior = {
28
+ RETRYABLE : 'RETRYABLE' ,
29
+ NON_RETRYABLE : 'NON_RETRYABLE' ,
30
+ } as const ;
31
+
32
+ type NexusHandlerErrorRetryBehavior =
33
+ ( typeof NexusHandlerErrorRetryBehavior ) [ keyof typeof NexusHandlerErrorRetryBehavior ] ;
34
+
35
+ const [ encodeNexusHandlerErrorRetryBehavior , decodeNexusHandlerErrorRetryBehavior ] =
36
+ makeProtoEnumConverters <
37
+ temporal . api . enums . v1 . NexusHandlerErrorRetryBehavior ,
38
+ typeof temporal . api . enums . v1 . NexusHandlerErrorRetryBehavior ,
39
+ keyof typeof temporal . api . enums . v1 . NexusHandlerErrorRetryBehavior ,
40
+ typeof NexusHandlerErrorRetryBehavior ,
41
+ 'NEXUS_HANDLER_ERROR_RETRY_BEHAVIOR_'
42
+ > (
43
+ {
44
+ UNSPECIFIED : 0 ,
45
+ [ NexusHandlerErrorRetryBehavior . RETRYABLE ] : 1 ,
46
+ [ NexusHandlerErrorRetryBehavior . NON_RETRYABLE ] : 2 ,
47
+ } as const ,
48
+ 'NEXUS_HANDLER_ERROR_RETRY_BEHAVIOR_'
49
+ ) ;
50
+
21
51
function combineRegExp ( ...regexps : RegExp [ ] ) : RegExp {
22
52
return new RegExp ( regexps . map ( ( x ) => `(?:${ x . source } )` ) . join ( '|' ) ) ;
23
53
}
@@ -28,6 +58,8 @@ function combineRegExp(...regexps: RegExp[]): RegExp {
28
58
const CUTOFF_STACK_PATTERNS = combineRegExp (
29
59
/** Activity execution */
30
60
/ \s + a t A c t i v i t y \. e x e c u t e \( .* [ \\ / ] w o r k e r [ \\ / ] (?: s r c | l i b ) [ \\ / ] a c t i v i t y \. [ j t ] s : \d + : \d + \) / ,
61
+ /** Nexus execution */
62
+ / \s + a t N e x u s H a n d l e r \. i n v o k e U s e r C o d e \( .* [ \\ / ] w o r k e r [ \\ / ] (?: s r c | l i b ) [ \\ / ] n e x u s \. [ j t ] s : \d + : \d + \) / ,
31
63
/** Workflow activation */
32
64
/ \s + a t A c t i v a t o r \. \S + N e x t H a n d l e r \( .* [ \\ / ] w o r k f l o w [ \\ / ] (?: s r c | l i b ) [ \\ / ] i n t e r n a l s \. [ j t ] s : \d + : \d + \) / ,
33
65
/** Workflow run anything in context */
@@ -120,7 +152,7 @@ export class DefaultFailureConverter implements FailureConverter {
120
152
*
121
153
* Does not set common properties, that is done in {@link failureToError}.
122
154
*/
123
- failureToErrorInner ( failure : ProtoFailure , payloadConverter : PayloadConverter ) : TemporalFailure {
155
+ failureToErrorInner ( failure : ProtoFailure , payloadConverter : PayloadConverter ) : Error {
124
156
if ( failure . applicationFailureInfo ) {
125
157
return new ApplicationFailure (
126
158
failure . message ?? undefined ,
@@ -192,6 +224,38 @@ export class DefaultFailureConverter implements FailureConverter {
192
224
this . optionalFailureToOptionalError ( failure . cause , payloadConverter )
193
225
) ;
194
226
}
227
+ if ( failure . nexusHandlerFailureInfo ) {
228
+ if ( failure . cause == null ) {
229
+ throw new TypeError ( 'Missing failure cause on nexusHandlerFailureInfo' ) ;
230
+ }
231
+ let retryable : boolean | undefined = undefined ;
232
+ const retryBehavior = decodeNexusHandlerErrorRetryBehavior ( failure . nexusHandlerFailureInfo . retryBehavior ) ;
233
+ switch ( retryBehavior ) {
234
+ case 'RETRYABLE' :
235
+ retryable = true ;
236
+ break ;
237
+ case 'NON_RETRYABLE' :
238
+ retryable = false ;
239
+ break ;
240
+ }
241
+
242
+ return new nexus . HandlerError ( {
243
+ type : ( failure . nexusHandlerFailureInfo . type as nexus . HandlerErrorType ) ?? 'INTERNAL' ,
244
+ cause : this . failureToError ( failure . cause , payloadConverter ) ,
245
+ retryable,
246
+ } ) ;
247
+ }
248
+ if ( failure . nexusOperationExecutionFailureInfo ) {
249
+ return new NexusOperationFailure (
250
+ failure . nexusOperationExecutionFailureInfo . scheduledEventId ?. toNumber ( ) ,
251
+ // We assume these will always be set or gracefully set to empty strings.
252
+ failure . nexusOperationExecutionFailureInfo . endpoint ?? '' ,
253
+ failure . nexusOperationExecutionFailureInfo . service ?? '' ,
254
+ failure . nexusOperationExecutionFailureInfo . operation ?? '' ,
255
+ failure . nexusOperationExecutionFailureInfo . operationToken ?? undefined ,
256
+ this . optionalFailureToOptionalError ( failure . cause , payloadConverter )
257
+ ) ;
258
+ }
195
259
return new TemporalFailure (
196
260
failure . message ?? undefined ,
197
261
this . optionalFailureToOptionalError ( failure . cause , payloadConverter )
@@ -216,7 +280,9 @@ export class DefaultFailureConverter implements FailureConverter {
216
280
}
217
281
const err = this . failureToErrorInner ( failure , payloadConverter ) ;
218
282
err . stack = failure . stackTrace ?? '' ;
219
- err . failure = failure ;
283
+ if ( err instanceof TemporalFailure ) {
284
+ err . failure = failure ;
285
+ }
220
286
return err ;
221
287
}
222
288
@@ -232,8 +298,8 @@ export class DefaultFailureConverter implements FailureConverter {
232
298
}
233
299
234
300
errorToFailureInner ( err : unknown , payloadConverter : PayloadConverter ) : ProtoFailure {
235
- if ( err instanceof TemporalFailure ) {
236
- if ( err . failure ) return err . failure ;
301
+ if ( err instanceof TemporalFailure || err instanceof nexus . HandlerError ) {
302
+ if ( err instanceof TemporalFailure && err . failure ) return err . failure ;
237
303
const base = {
238
304
message : err . message ,
239
305
stackTrace : cutoffStackTrace ( err . stack ) ,
@@ -310,6 +376,34 @@ export class DefaultFailureConverter implements FailureConverter {
310
376
terminatedFailureInfo : { } ,
311
377
} ;
312
378
}
379
+ if ( err instanceof nexus . HandlerError ) {
380
+ let retryBehavior : temporal . api . enums . v1 . NexusHandlerErrorRetryBehavior | undefined = undefined ;
381
+ if ( err . retryable === true ) {
382
+ retryBehavior = encodeNexusHandlerErrorRetryBehavior ( "RETRYABLE" ) ;
383
+ } else if ( err . retryable === false ) {
384
+ retryBehavior = encodeNexusHandlerErrorRetryBehavior ( "NON_RETRYABLE" ) ;
385
+ }
386
+
387
+ return {
388
+ ...base ,
389
+ nexusHandlerFailureInfo : {
390
+ type : err . type ,
391
+ retryBehavior,
392
+ } ,
393
+ } ;
394
+ }
395
+ if ( err instanceof NexusOperationFailure ) {
396
+ return {
397
+ ...base ,
398
+ nexusOperationExecutionFailureInfo : {
399
+ scheduledEventId : err . scheduledEventId ? Long . fromNumber ( err . scheduledEventId ) : undefined ,
400
+ endpoint : err . endpoint ,
401
+ service : err . service ,
402
+ operation : err . operation ,
403
+ operationToken : err . operationToken ,
404
+ } ,
405
+ } ;
406
+ }
313
407
// Just a TemporalFailure
314
408
return base ;
315
409
}
0 commit comments