@temporalio/workflow 1.14.2 → 1.15.1
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/lib/cancellation-scope.d.ts +2 -6
- package/lib/cancellation-scope.js +2 -9
- package/lib/cancellation-scope.js.map +1 -1
- package/lib/flags.js +1 -1
- package/lib/flags.js.map +1 -1
- package/lib/global-attributes.d.ts +17 -5
- package/lib/global-attributes.js +9 -17
- package/lib/global-attributes.js.map +1 -1
- package/lib/global-overrides.js +6 -6
- package/lib/global-overrides.js.map +1 -1
- package/lib/index.d.ts +4 -4
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/interceptors.d.ts +16 -1
- package/lib/interfaces.d.ts +73 -30
- package/lib/interfaces.js +54 -14
- package/lib/interfaces.js.map +1 -1
- package/lib/internals.d.ts +3 -1
- package/lib/internals.js +25 -7
- package/lib/internals.js.map +1 -1
- package/lib/metrics.js +4 -0
- package/lib/metrics.js.map +1 -1
- package/lib/nexus.d.ts +51 -0
- package/lib/nexus.js +68 -8
- package/lib/nexus.js.map +1 -1
- package/lib/stack-helpers.js +1 -1
- package/lib/stack-helpers.js.map +1 -1
- package/lib/update-scope.d.ts +0 -4
- package/lib/update-scope.js +0 -7
- package/lib/update-scope.js.map +1 -1
- package/lib/worker-interface.d.ts +2 -1
- package/lib/worker-interface.js +59 -10
- package/lib/worker-interface.js.map +1 -1
- package/lib/workflow.js +11 -11
- package/lib/workflow.js.map +1 -1
- package/package.json +4 -4
- package/src/cancellation-scope.ts +3 -10
- package/src/flags.ts +1 -1
- package/src/global-attributes.ts +34 -16
- package/src/global-overrides.ts +6 -6
- package/src/index.ts +11 -6
- package/src/interceptors.ts +18 -1
- package/src/interfaces.ts +84 -38
- package/src/internals.ts +30 -8
- package/src/metrics.ts +7 -0
- package/src/nexus.ts +84 -8
- package/src/stack-helpers.ts +2 -3
- package/src/update-scope.ts +1 -8
- package/src/worker-interface.ts +60 -13
- package/src/workflow.ts +11 -11
package/src/interceptors.ts
CHANGED
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
WorkflowExecution,
|
|
16
16
|
} from '@temporalio/common';
|
|
17
17
|
import type { coresdk } from '@temporalio/proto';
|
|
18
|
-
import { ChildWorkflowOptionsWithDefaults, ContinueAsNewOptions } from './interfaces';
|
|
18
|
+
import type { ChildWorkflowOptionsWithDefaults, ContinueAsNewOptions } from './interfaces';
|
|
19
|
+
import type { NexusOperationCancellationType } from './nexus';
|
|
19
20
|
|
|
20
21
|
export { Next, Headers };
|
|
21
22
|
|
|
@@ -288,6 +289,22 @@ export interface StartNexusOperationOptions {
|
|
|
288
289
|
*/
|
|
289
290
|
readonly scheduleToCloseTimeout?: Duration;
|
|
290
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Determines:
|
|
294
|
+
* - whether cancellation requests should be propagated from the Workflow to the Nexus Operation
|
|
295
|
+
* - whether and when should the Operation's cancellation be reported back to the Workflow
|
|
296
|
+
* (i.e. at which moment should the operation's result promise fail with a `NexusOperationFailure`,
|
|
297
|
+
* with `cause` set to a `CancelledFailure`).
|
|
298
|
+
*
|
|
299
|
+
* Note that this setting only applies to cancellation originating from an external request for the
|
|
300
|
+
* Workflow itself, or from internal cancellation of the `CancellationScope` in which the
|
|
301
|
+
* Operation call was made.
|
|
302
|
+
*
|
|
303
|
+
* @default WAIT_CANCELLATION_COMPLETED
|
|
304
|
+
*/
|
|
305
|
+
// MAINTENANCE: Keep this typedoc in sync with the `NexusOperationCancellationType` enum
|
|
306
|
+
readonly cancellationType?: NexusOperationCancellationType;
|
|
307
|
+
|
|
291
308
|
/**
|
|
292
309
|
* A fixed, single-line summary for this Nexus Operation that may appear in the UI/CLI.
|
|
293
310
|
* This can be in single-line Temporal markdown format.
|
package/src/interfaces.ts
CHANGED
|
@@ -45,7 +45,7 @@ export interface WorkflowInfo {
|
|
|
45
45
|
* This value may change during the lifetime of an Execution.
|
|
46
46
|
* @deprecated Use {@link typedSearchAttributes} instead.
|
|
47
47
|
*/
|
|
48
|
-
readonly searchAttributes: SearchAttributes; // eslint-disable-line
|
|
48
|
+
readonly searchAttributes: SearchAttributes; // eslint-disable-line @typescript-eslint/no-deprecated
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Indexed information attached to the Workflow Execution, exposed through an interface.
|
|
@@ -208,8 +208,6 @@ export interface WorkflowInfo {
|
|
|
208
208
|
* executing this task for the first time and has a Deployment Version set, then its ID will be
|
|
209
209
|
* used. This value may change over the lifetime of the workflow run, but is deterministic and
|
|
210
210
|
* safe to use for branching.
|
|
211
|
-
*
|
|
212
|
-
* @experimental Deployment based versioning is experimental and may change in the future.
|
|
213
211
|
*/
|
|
214
212
|
readonly currentDeploymentVersion?: WorkerDeploymentVersion;
|
|
215
213
|
|
|
@@ -235,7 +233,22 @@ export interface UnsafeWorkflowInfo {
|
|
|
235
233
|
*/
|
|
236
234
|
readonly now: () => number;
|
|
237
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Whether the workflow is currently replaying.
|
|
238
|
+
*/
|
|
238
239
|
readonly isReplaying: boolean;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Whether the workflow is currently replaying history events.
|
|
243
|
+
*
|
|
244
|
+
* This is similar to {@link isReplaying}, but returns `false` during query handlers and update
|
|
245
|
+
* validators, which are live read-only operations that should not be considered as replaying
|
|
246
|
+
* history events.
|
|
247
|
+
*
|
|
248
|
+
* When this property is true, workflow log messages are suppressed and sinks defined with
|
|
249
|
+
* callDuringReplay=false won't get processed.
|
|
250
|
+
*/
|
|
251
|
+
readonly isReplayingHistoryEvents: boolean;
|
|
239
252
|
}
|
|
240
253
|
|
|
241
254
|
/**
|
|
@@ -304,7 +317,7 @@ export interface ContinueAsNewOptions {
|
|
|
304
317
|
* Searchable attributes to attach to next Workflow run
|
|
305
318
|
* @deprecated Use {@link typedSearchAttributes} instead.
|
|
306
319
|
*/
|
|
307
|
-
searchAttributes?: SearchAttributes; // eslint-disable-line
|
|
320
|
+
searchAttributes?: SearchAttributes; // eslint-disable-line @typescript-eslint/no-deprecated
|
|
308
321
|
/**
|
|
309
322
|
* Specifies additional indexed information to attach to the Workflow Execution. More info:
|
|
310
323
|
* https://docs.temporal.io/docs/typescript/search-attributes
|
|
@@ -322,48 +335,74 @@ export interface ContinueAsNewOptions {
|
|
|
322
335
|
*
|
|
323
336
|
* @default 'COMPATIBLE'
|
|
324
337
|
*
|
|
325
|
-
* @deprecated
|
|
326
|
-
* @experimental The Worker Versioning API is still being designed. Major changes are expected.
|
|
338
|
+
* @deprecated Worker Versioning is now deprecated. Please use the Worker Deployment API instead: https://docs.temporal.io/worker-deployments
|
|
327
339
|
*/
|
|
328
|
-
versioningIntent?: VersioningIntent; // eslint-disable-line
|
|
340
|
+
versioningIntent?: VersioningIntent; // eslint-disable-line @typescript-eslint/no-deprecated
|
|
329
341
|
}
|
|
330
342
|
|
|
331
343
|
/**
|
|
332
|
-
*
|
|
333
|
-
* - whether cancellation requests
|
|
334
|
-
* - whether and when
|
|
335
|
-
* {@link ChildWorkflowHandle.result}
|
|
344
|
+
* Determines:
|
|
345
|
+
* - whether cancellation requests should be propagated from the Parent Workflow to the Child, and
|
|
346
|
+
* - whether and when should the Child's cancellation be reported back to the Parent Workflow
|
|
347
|
+
* (i.e. at which moment should the {@link executeChild}'s or {@link ChildWorkflowHandle.result}'s
|
|
348
|
+
* promise fail with a `ChildWorkflowFailure`, with `cause` set to a `CancelledFailure`).
|
|
349
|
+
*
|
|
350
|
+
* Note that this setting only applies to cancellation originating from an external request for the
|
|
351
|
+
* Parent Workflow itself, or from internal cancellation of the `CancellationScope` in which the
|
|
352
|
+
* Child Workflow call was made. Eventual Cancellation of a Child Workflow on completion of the
|
|
353
|
+
* Parent Workflow is controlled by the {@link ParentClosePolicy} setting.
|
|
336
354
|
*
|
|
337
|
-
* @default
|
|
355
|
+
* @default ChildWorkflowCancellationType.WAIT_CANCELLATION_COMPLETED
|
|
338
356
|
*/
|
|
339
|
-
|
|
340
|
-
(typeof ChildWorkflowCancellationType)[keyof typeof ChildWorkflowCancellationType];
|
|
357
|
+
// MAINTENANCE: Keep this typedoc in sync with the `ChildWorkflowOptions.cancellationType` field
|
|
341
358
|
export const ChildWorkflowCancellationType = {
|
|
342
359
|
/**
|
|
343
|
-
*
|
|
360
|
+
* Do not propagate cancellation requests to the Child, and immediately report cancellation
|
|
361
|
+
* to the caller.
|
|
344
362
|
*/
|
|
345
363
|
ABANDON: 'ABANDON',
|
|
346
364
|
|
|
347
365
|
/**
|
|
348
|
-
*
|
|
366
|
+
* Propagate cancellation request from the Parent Workflow to the Child, yet _immediately_ report
|
|
367
|
+
* cancellation to the caller, i.e. without waiting for the server to confirm the cancellation
|
|
368
|
+
* request.
|
|
369
|
+
*
|
|
370
|
+
* Note that this cancellation type provides no guarantee, from the Parent-side, that the
|
|
371
|
+
* cancellation request will actually be atomically added to the Child workflow's history.
|
|
372
|
+
* In particular, the Child may complete (either successfully or uncessfully) before the
|
|
373
|
+
* cancellation is delivered, resulting in a situation where the Parent workflow thinks its child
|
|
374
|
+
* was cancelled, but the child actually completed successfully.
|
|
375
|
+
*
|
|
376
|
+
* To guarantee that the Child will eventually be notified of the cancellation request,
|
|
377
|
+
* use {@link WAIT_CANCELLATION_REQUESTED}.
|
|
349
378
|
*/
|
|
350
379
|
TRY_CANCEL: 'TRY_CANCEL',
|
|
351
380
|
|
|
352
381
|
/**
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
* ignore the cancellation request, in which case an error might be thrown with a different cause, or the Child may
|
|
356
|
-
* complete successfully.
|
|
382
|
+
* Propagate cancellation request from the Parent Workflow to the Child, then wait for the server
|
|
383
|
+
* to confirm that the Child Workflow cancellation request was recorded in its history.
|
|
357
384
|
*
|
|
358
|
-
*
|
|
385
|
+
* This cancellation type guarantees that the Child will eventually be notified of the
|
|
386
|
+
* cancellation request (that is, unless the Child terminates inbetween due to unexpected causes).
|
|
359
387
|
*/
|
|
360
|
-
|
|
388
|
+
WAIT_CANCELLATION_REQUESTED: 'WAIT_CANCELLATION_REQUESTED',
|
|
361
389
|
|
|
362
390
|
/**
|
|
363
|
-
*
|
|
391
|
+
* Propagate cancellation request from the Parent Workflow to the Child, then wait for completion
|
|
392
|
+
* of the Child Workflow.
|
|
393
|
+
*
|
|
394
|
+
* The Child may respect cancellation, in which case the Parent's `executeChild` or
|
|
395
|
+
* `ChildWorkflowHandle.result` promise will fail with a `ChildWorkflowFailure`, with `cause`
|
|
396
|
+
* set to a `CancelledFailure`. On the other hand, the Child may ignore the cancellation request,
|
|
397
|
+
* in which case the corresponding promise will either resolve with a result (if Child completed
|
|
398
|
+
* successfully) or reject with a different cause (if Child completed uncessfully).
|
|
399
|
+
*
|
|
400
|
+
* @default
|
|
364
401
|
*/
|
|
365
|
-
|
|
402
|
+
WAIT_CANCELLATION_COMPLETED: 'WAIT_CANCELLATION_COMPLETED',
|
|
366
403
|
} as const;
|
|
404
|
+
export type ChildWorkflowCancellationType =
|
|
405
|
+
(typeof ChildWorkflowCancellationType)[keyof typeof ChildWorkflowCancellationType];
|
|
367
406
|
|
|
368
407
|
// ts-prune-ignore-next
|
|
369
408
|
export const [encodeChildWorkflowCancellationType, decodeChildWorkflowCancellationType] = makeProtoEnumConverters<
|
|
@@ -387,7 +426,6 @@ export const [encodeChildWorkflowCancellationType, decodeChildWorkflowCancellati
|
|
|
387
426
|
*
|
|
388
427
|
* @see {@link https://docs.temporal.io/concepts/what-is-a-parent-close-policy/ | Parent Close Policy}
|
|
389
428
|
*/
|
|
390
|
-
export type ParentClosePolicy = (typeof ParentClosePolicy)[keyof typeof ParentClosePolicy];
|
|
391
429
|
export const ParentClosePolicy = {
|
|
392
430
|
/**
|
|
393
431
|
* When the Parent is Closed, the Child is Terminated.
|
|
@@ -413,29 +451,30 @@ export const ParentClosePolicy = {
|
|
|
413
451
|
*
|
|
414
452
|
* @deprecated Either leave property `undefined`, or set an explicit policy instead.
|
|
415
453
|
*/
|
|
416
|
-
PARENT_CLOSE_POLICY_UNSPECIFIED: undefined,
|
|
454
|
+
PARENT_CLOSE_POLICY_UNSPECIFIED: undefined,
|
|
417
455
|
|
|
418
456
|
/**
|
|
419
457
|
* When the Parent is Closed, the Child is Terminated.
|
|
420
458
|
*
|
|
421
459
|
* @deprecated Use {@link ParentClosePolicy.TERMINATE} instead.
|
|
422
460
|
*/
|
|
423
|
-
PARENT_CLOSE_POLICY_TERMINATE: 'TERMINATE',
|
|
461
|
+
PARENT_CLOSE_POLICY_TERMINATE: 'TERMINATE',
|
|
424
462
|
|
|
425
463
|
/**
|
|
426
464
|
* When the Parent is Closed, nothing is done to the Child.
|
|
427
465
|
*
|
|
428
466
|
* @deprecated Use {@link ParentClosePolicy.ABANDON} instead.
|
|
429
467
|
*/
|
|
430
|
-
PARENT_CLOSE_POLICY_ABANDON: 'ABANDON',
|
|
468
|
+
PARENT_CLOSE_POLICY_ABANDON: 'ABANDON',
|
|
431
469
|
|
|
432
470
|
/**
|
|
433
471
|
* When the Parent is Closed, the Child is Cancelled.
|
|
434
472
|
*
|
|
435
473
|
* @deprecated Use {@link ParentClosePolicy.REQUEST_CANCEL} instead.
|
|
436
474
|
*/
|
|
437
|
-
PARENT_CLOSE_POLICY_REQUEST_CANCEL: 'REQUEST_CANCEL',
|
|
475
|
+
PARENT_CLOSE_POLICY_REQUEST_CANCEL: 'REQUEST_CANCEL',
|
|
438
476
|
} as const;
|
|
477
|
+
export type ParentClosePolicy = (typeof ParentClosePolicy)[keyof typeof ParentClosePolicy];
|
|
439
478
|
|
|
440
479
|
// ts-prune-ignore-next
|
|
441
480
|
export const [encodeParentClosePolicy, decodeParentClosePolicy] = makeProtoEnumConverters<
|
|
@@ -471,19 +510,26 @@ export interface ChildWorkflowOptions extends Omit<CommonWorkflowOptions, 'workf
|
|
|
471
510
|
taskQueue?: string;
|
|
472
511
|
|
|
473
512
|
/**
|
|
474
|
-
*
|
|
475
|
-
* - whether cancellation requests
|
|
476
|
-
* - whether and when
|
|
477
|
-
* {@link ChildWorkflowHandle.result}
|
|
513
|
+
* Determines:
|
|
514
|
+
* - whether cancellation requests should be propagated from the Parent Workflow to the Child, and
|
|
515
|
+
* - whether and when should the Child's cancellation be reported back to the Parent Workflow
|
|
516
|
+
* (i.e. at which moment should the {@link executeChild}'s or {@link ChildWorkflowHandle.result}'s
|
|
517
|
+
* promise fail with a `ChildWorkflowFailure`, with `cause` set to a `CancelledFailure`).
|
|
518
|
+
*
|
|
519
|
+
* Note that this setting only applies to cancellation originating from an external request for the
|
|
520
|
+
* Parent Workflow itself, or from internal cancellation of the `CancellationScope` in which the
|
|
521
|
+
* Child Workflow call was made. Eventual Cancellation of a Child Workflow on completion of the
|
|
522
|
+
* Parent Workflow is controlled by the {@link ParentClosePolicy} setting.
|
|
478
523
|
*
|
|
479
|
-
* @default
|
|
524
|
+
* @default ChildWorkflowCancellationType.WAIT_CANCELLATION_COMPLETED
|
|
480
525
|
*/
|
|
526
|
+
// MAINTENANCE: Keep this typedoc in sync with the `ChildWorkflowCancellationType` enum
|
|
481
527
|
cancellationType?: ChildWorkflowCancellationType;
|
|
482
528
|
|
|
483
529
|
/**
|
|
484
530
|
* Specifies how the Child reacts to the Parent Workflow reaching a Closed state.
|
|
485
531
|
*
|
|
486
|
-
* @default
|
|
532
|
+
* @default ParentClosePolicy.PARENT_CLOSE_POLICY_TERMINATE
|
|
487
533
|
*/
|
|
488
534
|
parentClosePolicy?: ParentClosePolicy;
|
|
489
535
|
|
|
@@ -493,10 +539,9 @@ export interface ChildWorkflowOptions extends Omit<CommonWorkflowOptions, 'workf
|
|
|
493
539
|
*
|
|
494
540
|
* @default 'COMPATIBLE'
|
|
495
541
|
*
|
|
496
|
-
* @deprecated
|
|
497
|
-
* @experimental The Worker Versioning API is still being designed. Major changes are expected.
|
|
542
|
+
* @deprecated Worker Versioning is now deprecated. Please use the Worker Deployment API instead: https://docs.temporal.io/worker-deployments
|
|
498
543
|
*/
|
|
499
|
-
versioningIntent?: VersioningIntent; // eslint-disable-line
|
|
544
|
+
versioningIntent?: VersioningIntent; // eslint-disable-line @typescript-eslint/no-deprecated
|
|
500
545
|
}
|
|
501
546
|
|
|
502
547
|
export type RequiredChildWorkflowOptions = Required<Pick<ChildWorkflowOptions, 'workflowId' | 'cancellationType'>> & {
|
|
@@ -581,6 +626,7 @@ export interface WorkflowCreateOptionsInternal extends WorkflowCreateOptions {
|
|
|
581
626
|
sourceMap: RawSourceMap;
|
|
582
627
|
registeredActivityNames: Set<string>;
|
|
583
628
|
getTimeOfDay(): bigint;
|
|
629
|
+
stackTracesEnabled: boolean;
|
|
584
630
|
}
|
|
585
631
|
|
|
586
632
|
/**
|
package/src/internals.ts
CHANGED
|
@@ -455,6 +455,10 @@ export class Activator implements ActivationHandler {
|
|
|
455
455
|
public versioningBehavior?: VersioningBehavior;
|
|
456
456
|
public workflowDefinitionOptionsGetter?: () => WorkflowDefinitionOptions;
|
|
457
457
|
|
|
458
|
+
public readonly workflowSandboxDestructors: (() => void)[] = [];
|
|
459
|
+
|
|
460
|
+
protected readonly stackTracesEnabled: boolean;
|
|
461
|
+
|
|
458
462
|
constructor({
|
|
459
463
|
info,
|
|
460
464
|
now,
|
|
@@ -463,6 +467,7 @@ export class Activator implements ActivationHandler {
|
|
|
463
467
|
getTimeOfDay,
|
|
464
468
|
randomnessSeed,
|
|
465
469
|
registeredActivityNames,
|
|
470
|
+
stackTracesEnabled,
|
|
466
471
|
}: WorkflowCreateOptionsInternal) {
|
|
467
472
|
this.getTimeOfDay = getTimeOfDay;
|
|
468
473
|
this.info = info;
|
|
@@ -471,6 +476,7 @@ export class Activator implements ActivationHandler {
|
|
|
471
476
|
this.sourceMap = sourceMap;
|
|
472
477
|
this.random = alea(randomnessSeed);
|
|
473
478
|
this.registeredActivityNames = registeredActivityNames;
|
|
479
|
+
this.stackTracesEnabled = stackTracesEnabled;
|
|
474
480
|
}
|
|
475
481
|
|
|
476
482
|
/**
|
|
@@ -481,6 +487,9 @@ export class Activator implements ActivationHandler {
|
|
|
481
487
|
}
|
|
482
488
|
|
|
483
489
|
protected getStackTraces(): Stack[] {
|
|
490
|
+
if (!this.stackTracesEnabled) {
|
|
491
|
+
throw new IllegalStateError('Workflow stack traces are not enabled on this worker');
|
|
492
|
+
}
|
|
484
493
|
const { childToParent, promiseToStack } = this.promiseStackStore;
|
|
485
494
|
const internalNodes = [...childToParent.values()].reduce((acc, curr) => {
|
|
486
495
|
for (const p of curr) {
|
|
@@ -856,12 +865,26 @@ export class Activator implements ActivationHandler {
|
|
|
856
865
|
let input: UpdateInput;
|
|
857
866
|
try {
|
|
858
867
|
if (runValidator && entry.validator) {
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
868
|
+
// Temporarily mark as not replaying history events during validator execution
|
|
869
|
+
// so that logging is permitted. Validators are live read-only operations.
|
|
870
|
+
const wasReplayingHistoryEvents = this.info.unsafe.isReplayingHistoryEvents;
|
|
871
|
+
this.mutateWorkflowInfo((info) => ({
|
|
872
|
+
...info,
|
|
873
|
+
unsafe: { ...info.unsafe, isReplayingHistoryEvents: false },
|
|
874
|
+
}));
|
|
875
|
+
try {
|
|
876
|
+
const validate = composeInterceptors(
|
|
877
|
+
interceptors,
|
|
878
|
+
'validateUpdate',
|
|
879
|
+
this.validateUpdateNextHandler.bind(this, entry.validator)
|
|
880
|
+
);
|
|
881
|
+
validate(makeInput());
|
|
882
|
+
} finally {
|
|
883
|
+
this.mutateWorkflowInfo((info) => ({
|
|
884
|
+
...info,
|
|
885
|
+
unsafe: { ...info.unsafe, isReplayingHistoryEvents: wasReplayingHistoryEvents },
|
|
886
|
+
}));
|
|
887
|
+
}
|
|
865
888
|
}
|
|
866
889
|
input = makeInput();
|
|
867
890
|
} catch (error) {
|
|
@@ -928,7 +951,6 @@ export class Activator implements ActivationHandler {
|
|
|
928
951
|
const update = this.bufferedUpdates.shift();
|
|
929
952
|
if (update) {
|
|
930
953
|
this.rejectUpdate(
|
|
931
|
-
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
932
954
|
update.protocolInstanceId!,
|
|
933
955
|
ApplicationFailure.nonRetryable(`No registered handler for update: ${update.name}`)
|
|
934
956
|
);
|
|
@@ -995,7 +1017,7 @@ export class Activator implements ActivationHandler {
|
|
|
995
1017
|
while (bufferedSignals.length) {
|
|
996
1018
|
if (this.defaultSignalHandler) {
|
|
997
1019
|
// We have a default signal handler, so all signals are dispatchable
|
|
998
|
-
|
|
1020
|
+
|
|
999
1021
|
this.signalWorkflow(bufferedSignals.shift()!);
|
|
1000
1022
|
} else {
|
|
1001
1023
|
const foundIndex = bufferedSignals.findIndex((signal) => this.signalHandlers.has(signal.signalName as string));
|
package/src/metrics.ts
CHANGED
|
@@ -48,6 +48,9 @@ class WorkflowMetricMeterImpl implements MetricMeter {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
class WorkflowMetricCounter implements MetricCounter {
|
|
51
|
+
public readonly kind = 'counter';
|
|
52
|
+
public readonly valueType = 'int';
|
|
53
|
+
|
|
51
54
|
constructor(
|
|
52
55
|
public readonly name: string,
|
|
53
56
|
public readonly unit: string | undefined,
|
|
@@ -70,6 +73,8 @@ class WorkflowMetricCounter implements MetricCounter {
|
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
class WorkflowMetricHistogram implements MetricHistogram {
|
|
76
|
+
public readonly kind = 'histogram';
|
|
77
|
+
|
|
73
78
|
constructor(
|
|
74
79
|
public readonly name: string,
|
|
75
80
|
public readonly valueType: NumericMetricValueType,
|
|
@@ -93,6 +98,8 @@ class WorkflowMetricHistogram implements MetricHistogram {
|
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
class WorkflowMetricGauge implements MetricGauge {
|
|
101
|
+
public readonly kind = 'gauge';
|
|
102
|
+
|
|
96
103
|
constructor(
|
|
97
104
|
public readonly name: string,
|
|
98
105
|
public readonly valueType: NumericMetricValueType,
|
package/src/nexus.ts
CHANGED
|
@@ -2,6 +2,8 @@ import * as nexus from 'nexus-rpc';
|
|
|
2
2
|
import { msOptionalToTs } from '@temporalio/common/lib/time';
|
|
3
3
|
import { userMetadataToPayload } from '@temporalio/common/lib/user-metadata';
|
|
4
4
|
import { composeInterceptors } from '@temporalio/common/lib/interceptors';
|
|
5
|
+
import { makeProtoEnumConverters } from '@temporalio/common/lib/internal-workflow/enums-helpers';
|
|
6
|
+
import type { coresdk } from '@temporalio/proto';
|
|
5
7
|
import { CancellationScope } from './cancellation-scope';
|
|
6
8
|
import { getActivator } from './global-attributes';
|
|
7
9
|
import { untrackPromise } from './stack-helpers';
|
|
@@ -146,17 +148,18 @@ export function createNexusClient<T extends nexus.ServiceDefinition>(options: Ne
|
|
|
146
148
|
startNexusOperationNextHandler
|
|
147
149
|
);
|
|
148
150
|
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
// That's totally different from what we did in ChildWorkflow, but I think that's cleaner from
|
|
154
|
-
// interceptors point of view, and will make it easier to extend the API in the future.
|
|
151
|
+
// The interceptor returns a Promise<StartNexusOperationOutput>, with the result promise contained
|
|
152
|
+
// in that Output object. As a consequence of this, the result promise/completion does not exist
|
|
153
|
+
// until the StartNexusOperation event is received. This is totally different from what we did in
|
|
154
|
+
// ChildWorkflow, but is much cleaner from the interceptors point of view.
|
|
155
155
|
const { token, result: resultPromise } = await execute({
|
|
156
156
|
endpoint: options.endpoint,
|
|
157
157
|
service: options.service.name,
|
|
158
158
|
operation: opName,
|
|
159
|
-
options:
|
|
159
|
+
options: {
|
|
160
|
+
...operationOptions,
|
|
161
|
+
cancellationType: operationOptions?.cancellationType ?? 'WAIT_CANCELLATION_COMPLETED',
|
|
162
|
+
},
|
|
160
163
|
headers: {},
|
|
161
164
|
seq,
|
|
162
165
|
input,
|
|
@@ -220,7 +223,7 @@ function startNexusOperationNextHandler({
|
|
|
220
223
|
nexusHeader: headers,
|
|
221
224
|
input: activator.payloadConverter.toPayload(input),
|
|
222
225
|
scheduleToCloseTimeout: msOptionalToTs(options?.scheduleToCloseTimeout),
|
|
223
|
-
|
|
226
|
+
cancellationType: encodeNexusOperationCancellationType(options?.cancellationType),
|
|
224
227
|
},
|
|
225
228
|
userMetadata: userMetadataToPayload(activator.payloadConverter, options?.summary, undefined),
|
|
226
229
|
});
|
|
@@ -231,3 +234,76 @@ function startNexusOperationNextHandler({
|
|
|
231
234
|
});
|
|
232
235
|
});
|
|
233
236
|
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Determines:
|
|
240
|
+
* - whether cancellation requests should be propagated from the Workflow to the Nexus Operation
|
|
241
|
+
* - whether and when should the Operation's cancellation be reported back to the Workflow
|
|
242
|
+
* (i.e. at which moment should the operation's result promise fail with a `NexusOperationFailure`,
|
|
243
|
+
* with `cause` set to a `CancelledFailure`).
|
|
244
|
+
*
|
|
245
|
+
* Note that this setting only applies to cancellation originating from an external request for the
|
|
246
|
+
* Workflow itself, or from internal cancellation of the `CancellationScope` in which the
|
|
247
|
+
* Operation call was made.
|
|
248
|
+
*
|
|
249
|
+
* @experimental Nexus support in Temporal SDK is experimental.
|
|
250
|
+
*/
|
|
251
|
+
// MAINTENANCE: Keep this typedoc in sync with the `StartNexusOperationOptions.cancellationType` field
|
|
252
|
+
export const NexusOperationCancellationType = {
|
|
253
|
+
/**
|
|
254
|
+
* Do not propagate cancellation requests to the Nexus Operation, and immediately report
|
|
255
|
+
* cancellation to the caller.
|
|
256
|
+
*/
|
|
257
|
+
ABANDON: 'ABANDON',
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Initiate a cancellation request for the Nexus operation and immediately report cancellation to
|
|
261
|
+
* the caller. Note that it doesn't guarantee that cancellation is delivered to the operation if
|
|
262
|
+
* calling workflow exits before the delivery is done. If you want to ensure that cancellation is
|
|
263
|
+
* delivered to the operation, use {@link WAIT_CANCELLATION_REQUESTED}.
|
|
264
|
+
*
|
|
265
|
+
* Propagate cancellation request from the Workflow to the Operation, yet _immediately_ report
|
|
266
|
+
* cancellation to the caller, i.e. without waiting for the server to confirm the cancellation
|
|
267
|
+
* request.
|
|
268
|
+
*
|
|
269
|
+
* Note that this cancellation type provides no guarantee, from the Workflow-side, that the
|
|
270
|
+
* cancellation request will be delivered to the Operation Handler. In particular, either the
|
|
271
|
+
* Operation or the Workflow may complete (either successfully or uncessfully) before the
|
|
272
|
+
* cancellation request is delivered, resulting in a situation where the Operation completed
|
|
273
|
+
* successfully, but the Workflow thinks it was cancelled.
|
|
274
|
+
*
|
|
275
|
+
* To guarantee that the Operation will eventually be notified of the cancellation request,
|
|
276
|
+
* use {@link WAIT_CANCELLATION_REQUESTED}.
|
|
277
|
+
*/
|
|
278
|
+
TRY_CANCEL: 'TRY_CANCEL',
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Propagate cancellation request from the Workflow to the Operation, then wait for the server
|
|
282
|
+
* to confirm that the Operation cancellation request was delivered to the Operation Handler.
|
|
283
|
+
*/
|
|
284
|
+
WAIT_CANCELLATION_REQUESTED: 'WAIT_CANCELLATION_REQUESTED',
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Propagate cancellation request from the Workflow to the Operation, then wait for completion
|
|
288
|
+
* of the Operation.
|
|
289
|
+
*/
|
|
290
|
+
WAIT_CANCELLATION_COMPLETED: 'WAIT_CANCELLATION_COMPLETED',
|
|
291
|
+
} as const;
|
|
292
|
+
export type NexusOperationCancellationType =
|
|
293
|
+
(typeof NexusOperationCancellationType)[keyof typeof NexusOperationCancellationType];
|
|
294
|
+
|
|
295
|
+
const [encodeNexusOperationCancellationType, _] = makeProtoEnumConverters<
|
|
296
|
+
coresdk.nexus.NexusOperationCancellationType,
|
|
297
|
+
typeof coresdk.nexus.NexusOperationCancellationType,
|
|
298
|
+
keyof typeof coresdk.nexus.NexusOperationCancellationType,
|
|
299
|
+
typeof NexusOperationCancellationType,
|
|
300
|
+
''
|
|
301
|
+
>(
|
|
302
|
+
{
|
|
303
|
+
[NexusOperationCancellationType.WAIT_CANCELLATION_COMPLETED]: 0,
|
|
304
|
+
[NexusOperationCancellationType.ABANDON]: 1,
|
|
305
|
+
[NexusOperationCancellationType.TRY_CANCEL]: 2,
|
|
306
|
+
[NexusOperationCancellationType.WAIT_CANCELLATION_REQUESTED]: 3,
|
|
307
|
+
} as const,
|
|
308
|
+
''
|
|
309
|
+
);
|
package/src/stack-helpers.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { PromiseStackStore } from './internals';
|
|
1
|
+
import { maybeGetActivator } from './global-attributes';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Helper function to remove a promise from being tracked for stack trace query purposes
|
|
6
5
|
*/
|
|
7
6
|
export function untrackPromise(promise: Promise<unknown>): void {
|
|
8
|
-
const store = (
|
|
7
|
+
const store = maybeGetActivator()?.promiseStackStore;
|
|
9
8
|
if (!store) return;
|
|
10
9
|
store.childToParent.delete(promise);
|
|
11
10
|
store.promiseToStack.delete(promise);
|
package/src/update-scope.ts
CHANGED
|
@@ -57,11 +57,4 @@ export class UpdateScope {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
const storage = new AsyncLocalStorage
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Disable the async local storage for updates.
|
|
64
|
-
*/
|
|
65
|
-
export function disableUpdateStorage(): void {
|
|
66
|
-
storage.disable();
|
|
67
|
-
}
|
|
60
|
+
const storage: ALS<UpdateScope> = new AsyncLocalStorage();
|
package/src/worker-interface.ts
CHANGED
|
@@ -6,17 +6,14 @@
|
|
|
6
6
|
import { encodeVersioningBehavior, IllegalStateError, WorkflowFunctionWithOptions } from '@temporalio/common';
|
|
7
7
|
import { composeInterceptors } from '@temporalio/common/lib/interceptors';
|
|
8
8
|
import { coresdk } from '@temporalio/proto';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { WorkflowInterceptorsFactory } from './interceptors';
|
|
12
|
-
import { WorkflowCreateOptionsInternal } from './interfaces';
|
|
9
|
+
import type { WorkflowInterceptorsFactory } from './interceptors';
|
|
10
|
+
import type { WorkflowCreateOptionsInternal } from './interfaces';
|
|
13
11
|
import { Activator } from './internals';
|
|
14
|
-
import {
|
|
12
|
+
import { setActivator, getActivator, maybeGetActivator } from './global-attributes';
|
|
15
13
|
|
|
16
14
|
// Export the type for use on the "worker" side
|
|
17
15
|
export { PromiseStackStore } from './internals';
|
|
18
16
|
|
|
19
|
-
const global = globalThis as any;
|
|
20
17
|
const OriginalDate = globalThis.Date;
|
|
21
18
|
|
|
22
19
|
/**
|
|
@@ -35,7 +32,7 @@ export function initRuntime(options: WorkflowCreateOptionsInternal): void {
|
|
|
35
32
|
// There's one activator per workflow instance, set it globally on the context.
|
|
36
33
|
// We do this before importing any user code so user code can statically reference @temporalio/workflow functions
|
|
37
34
|
// as well as Date and Math.random.
|
|
38
|
-
|
|
35
|
+
setActivator(activator);
|
|
39
36
|
|
|
40
37
|
activator.rethrowSynchronously = true;
|
|
41
38
|
try {
|
|
@@ -54,7 +51,7 @@ export function initRuntime(options: WorkflowCreateOptionsInternal): void {
|
|
|
54
51
|
activator.failureConverter = customFailureConverter;
|
|
55
52
|
}
|
|
56
53
|
|
|
57
|
-
const { importWorkflows, importInterceptors } =
|
|
54
|
+
const { importWorkflows, importInterceptors } = globalThis.__TEMPORAL__;
|
|
58
55
|
if (importWorkflows === undefined || importInterceptors === undefined) {
|
|
59
56
|
throw new IllegalStateError('Workflow bundle did not register import hooks');
|
|
60
57
|
}
|
|
@@ -97,6 +94,15 @@ export function initRuntime(options: WorkflowCreateOptionsInternal): void {
|
|
|
97
94
|
activator.workflowDefinitionOptionsGetter = activator.workflow.workflowDefinitionOptions;
|
|
98
95
|
}
|
|
99
96
|
}
|
|
97
|
+
} catch (e) {
|
|
98
|
+
try {
|
|
99
|
+
// Core won't send an eviction job after an early error, so we are responsible for triggering
|
|
100
|
+
// disposal of the workflow execution context. Otherwise, there might be resource leaks.
|
|
101
|
+
dispose();
|
|
102
|
+
} catch (_innerError) {
|
|
103
|
+
// Ignore error disposing of activator, it is more important to rethrow the original error
|
|
104
|
+
}
|
|
105
|
+
throw e;
|
|
100
106
|
} finally {
|
|
101
107
|
activator.rethrowSynchronously = false;
|
|
102
108
|
}
|
|
@@ -252,17 +258,58 @@ export function tryUnblockConditions(): number {
|
|
|
252
258
|
}
|
|
253
259
|
}
|
|
254
260
|
|
|
261
|
+
// Handle disposal of the workflow execution context (not to be confused with destroying
|
|
262
|
+
// the sandbox vm, which may possibly be reused if `reuseV8Context` is true).
|
|
255
263
|
export function dispose(): void {
|
|
264
|
+
let error: unknown | undefined = undefined;
|
|
265
|
+
|
|
256
266
|
const activator = getActivator();
|
|
257
267
|
activator.rethrowSynchronously = true;
|
|
258
268
|
try {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
})
|
|
263
|
-
|
|
269
|
+
try {
|
|
270
|
+
const dispose = composeInterceptors(activator.interceptors.internals, 'dispose', async () => {});
|
|
271
|
+
dispose({});
|
|
272
|
+
} catch (e) {
|
|
273
|
+
error = e;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Destructors are run outside of interceptors, because interceptors themselves often rely
|
|
277
|
+
// on objects that will be destroyed (notably AsyncLocalStorage instances), and because we
|
|
278
|
+
// want to make sure that resources get cleaned up even if an interceptor throws an error.
|
|
279
|
+
// Only the first error (if any) is rethrown.
|
|
280
|
+
for (const destructor of activator.workflowSandboxDestructors.splice(0)) {
|
|
281
|
+
try {
|
|
282
|
+
destructor();
|
|
283
|
+
} catch (e) {
|
|
284
|
+
if (error == null) {
|
|
285
|
+
error = e;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (error != null) throw error;
|
|
264
291
|
} finally {
|
|
265
292
|
activator.rethrowSynchronously = false;
|
|
293
|
+
|
|
294
|
+
// The activator is no longer valid past this point.
|
|
295
|
+
setActivator(undefined);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Destroy the sandbox VM (not to be confused with disposing of the workflow execution context).
|
|
300
|
+
export function destroy(): void {
|
|
301
|
+
const activator = maybeGetActivator();
|
|
302
|
+
if (activator) throw new IllegalStateError('Workflow execution context should have been disposed first');
|
|
303
|
+
|
|
304
|
+
let error: unknown | undefined = undefined;
|
|
305
|
+
for (const destructor of globalThis.__temporal_globalSandboxDestructors?.splice(0) ?? []) {
|
|
306
|
+
try {
|
|
307
|
+
destructor();
|
|
308
|
+
} catch (e) {
|
|
309
|
+
if (error == null) {
|
|
310
|
+
error = e;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
266
313
|
}
|
|
267
314
|
}
|
|
268
315
|
|