@temporalio/workflow 0.21.0 → 1.0.0-rc.0

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/workflow.js CHANGED
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setHandler = exports.defineQuery = exports.defineSignal = exports.condition = exports.deprecatePatch = exports.patched = exports.uuid4 = exports.continueAsNew = exports.makeContinueAsNewFunc = exports.proxySinks = exports.workflowInfo = exports.executeChild = exports.startChild = exports.getExternalWorkflowHandle = exports.proxyLocalActivities = exports.proxyActivities = exports.scheduleLocalActivity = exports.scheduleActivity = exports.validateActivityOptions = exports.sleep = exports.addDefaultWorkflowOptions = void 0;
3
+ exports.stackTraceQuery = exports.taskInfo = exports.upsertSearchAttributes = exports.setHandler = exports.defineQuery = exports.defineSignal = exports.condition = exports.deprecatePatch = exports.patched = exports.uuid4 = exports.continueAsNew = exports.makeContinueAsNewFunc = exports.proxySinks = exports.inWorkflowContext = exports.workflowInfo = exports.executeChild = exports.startChild = exports.getExternalWorkflowHandle = exports.proxyLocalActivities = exports.proxyActivities = exports.scheduleLocalActivity = exports.scheduleActivity = exports.sleep = exports.addDefaultWorkflowOptions = void 0;
4
4
  const common_1 = require("@temporalio/common");
5
5
  const internal_workflow_common_1 = require("@temporalio/internal-workflow-common");
6
6
  const cancellation_scope_1 = require("./cancellation-scope");
7
7
  const interfaces_1 = require("./interfaces");
8
8
  const internals_1 = require("./internals");
9
+ const stack_helpers_1 = require("./stack-helpers");
9
10
  // Avoid a circular dependency
10
11
  (0, cancellation_scope_1.registerSleepImplementation)(sleep);
11
12
  /**
@@ -28,13 +29,13 @@ function timerNextHandler(input) {
28
29
  return new Promise((resolve, reject) => {
29
30
  const scope = cancellation_scope_1.CancellationScope.current();
30
31
  if (scope.consideredCancelled) {
31
- scope.cancelRequested.catch(reject);
32
+ (0, stack_helpers_1.untrackPromise)(scope.cancelRequested.catch(reject));
32
33
  return;
33
34
  }
34
35
  if (scope.cancellable) {
35
- scope.cancelRequested.catch((err) => {
36
+ (0, stack_helpers_1.untrackPromise)(scope.cancelRequested.catch((err) => {
36
37
  if (!internals_1.state.completions.timer.delete(input.seq)) {
37
- return; // Already resolved
38
+ return; // Already resolved or never scheduled
38
39
  }
39
40
  internals_1.state.pushCommand({
40
41
  cancelTimer: {
@@ -42,18 +43,18 @@ function timerNextHandler(input) {
42
43
  },
43
44
  });
44
45
  reject(err);
45
- });
46
+ }));
46
47
  }
47
- internals_1.state.completions.timer.set(input.seq, {
48
- resolve,
49
- reject,
50
- });
51
48
  internals_1.state.pushCommand({
52
49
  startTimer: {
53
50
  seq: input.seq,
54
51
  startToFireTimeout: (0, internal_workflow_common_1.msToTs)(input.durationMs),
55
52
  },
56
53
  });
54
+ internals_1.state.completions.timer.set(input.seq, {
55
+ resolve,
56
+ reject,
57
+ });
57
58
  });
58
59
  }
59
60
  /**
@@ -61,28 +62,24 @@ function timerNextHandler(input) {
61
62
  *
62
63
  * Schedules a timer on the Temporal service.
63
64
  *
64
- * @param ms sleep duration - {@link https://www.npmjs.com/package/ms | ms} formatted string or number of milliseconds. If given a negative number, value will be set to 1.
65
- *
65
+ * @param ms sleep duration - {@link https://www.npmjs.com/package/ms | ms} formatted string or number of milliseconds.
66
+ * If given a negative number or 0, value will be set to 1.
66
67
  */
67
68
  function sleep(ms) {
68
69
  const seq = internals_1.state.nextSeqs.timer++;
69
- const execute = (0, internal_workflow_common_1.composeInterceptors)(internals_1.state.interceptors.outbound, 'startTimer', timerNextHandler);
70
70
  const durationMs = Math.max(1, (0, internal_workflow_common_1.msToNumber)(ms));
71
+ const execute = (0, internal_workflow_common_1.composeInterceptors)(internals_1.state.interceptors.outbound, 'startTimer', timerNextHandler);
71
72
  return execute({
72
73
  durationMs,
73
74
  seq,
74
75
  });
75
76
  }
76
77
  exports.sleep = sleep;
77
- /**
78
- * @hidden
79
- */
80
78
  function validateActivityOptions(options) {
81
79
  if (options.scheduleToCloseTimeout === undefined && options.startToCloseTimeout === undefined) {
82
80
  throw new TypeError('Required either scheduleToCloseTimeout or startToCloseTimeout');
83
81
  }
84
82
  }
85
- exports.validateActivityOptions = validateActivityOptions;
86
83
  // Use same validation we use for normal activities
87
84
  const validateLocalActivityOptions = validateActivityOptions;
88
85
  /**
@@ -93,30 +90,26 @@ const validateLocalActivityOptions = validateActivityOptions;
93
90
  /**
94
91
  * Push a scheduleActivity command into state accumulator and register completion
95
92
  */
96
- async function scheduleActivityNextHandler({ options, args, headers, seq, activityType, }) {
93
+ function scheduleActivityNextHandler({ options, args, headers, seq, activityType }) {
97
94
  validateActivityOptions(options);
98
95
  return new Promise((resolve, reject) => {
99
96
  const scope = cancellation_scope_1.CancellationScope.current();
100
97
  if (scope.consideredCancelled) {
101
- scope.cancelRequested.catch(reject);
98
+ (0, stack_helpers_1.untrackPromise)(scope.cancelRequested.catch(reject));
102
99
  return;
103
100
  }
104
101
  if (scope.cancellable) {
105
- scope.cancelRequested.catch(() => {
102
+ (0, stack_helpers_1.untrackPromise)(scope.cancelRequested.catch(() => {
106
103
  if (!internals_1.state.completions.activity.has(seq)) {
107
- return; // Already resolved
104
+ return; // Already resolved or never scheduled
108
105
  }
109
106
  internals_1.state.pushCommand({
110
107
  requestCancelActivity: {
111
108
  seq,
112
109
  },
113
110
  });
114
- });
111
+ }));
115
112
  }
116
- internals_1.state.completions.activity.set(seq, {
117
- resolve,
118
- reject,
119
- });
120
113
  internals_1.state.pushCommand({
121
114
  scheduleActivity: {
122
115
  seq,
@@ -133,6 +126,10 @@ async function scheduleActivityNextHandler({ options, args, headers, seq, activi
133
126
  cancellationType: options.cancellationType,
134
127
  },
135
128
  });
129
+ internals_1.state.completions.activity.set(seq, {
130
+ resolve,
131
+ reject,
132
+ });
136
133
  });
137
134
  }
138
135
  /**
@@ -143,25 +140,21 @@ async function scheduleLocalActivityNextHandler({ options, args, headers, seq, a
143
140
  return new Promise((resolve, reject) => {
144
141
  const scope = cancellation_scope_1.CancellationScope.current();
145
142
  if (scope.consideredCancelled) {
146
- scope.cancelRequested.catch(reject);
143
+ (0, stack_helpers_1.untrackPromise)(scope.cancelRequested.catch(reject));
147
144
  return;
148
145
  }
149
146
  if (scope.cancellable) {
150
- scope.cancelRequested.catch(() => {
147
+ (0, stack_helpers_1.untrackPromise)(scope.cancelRequested.catch(() => {
151
148
  if (!internals_1.state.completions.activity.has(seq)) {
152
- return; // Already resolved
149
+ return; // Already resolved or never scheduled
153
150
  }
154
151
  internals_1.state.pushCommand({
155
152
  requestCancelLocalActivity: {
156
153
  seq,
157
154
  },
158
155
  });
159
- });
156
+ }));
160
157
  }
161
- internals_1.state.completions.activity.set(seq, {
162
- resolve,
163
- reject,
164
- });
165
158
  internals_1.state.pushCommand({
166
159
  scheduleLocalActivity: {
167
160
  seq,
@@ -171,7 +164,7 @@ async function scheduleLocalActivityNextHandler({ options, args, headers, seq, a
171
164
  activityId: `${seq}`,
172
165
  activityType,
173
166
  arguments: (0, common_1.toPayloads)(internals_1.state.payloadConverter, ...args),
174
- retryPolicy: options.retry ? (0, internal_workflow_common_1.compileRetryPolicy)(options.retry) : {},
167
+ retryPolicy: options.retry ? (0, internal_workflow_common_1.compileRetryPolicy)(options.retry) : undefined,
175
168
  scheduleToCloseTimeout: (0, internal_workflow_common_1.msOptionalToTs)(options.scheduleToCloseTimeout),
176
169
  startToCloseTimeout: (0, internal_workflow_common_1.msOptionalToTs)(options.startToCloseTimeout),
177
170
  scheduleToStartTimeout: (0, internal_workflow_common_1.msOptionalToTs)(options.scheduleToStartTimeout),
@@ -180,6 +173,10 @@ async function scheduleLocalActivityNextHandler({ options, args, headers, seq, a
180
173
  cancellationType: options.cancellationType,
181
174
  },
182
175
  });
176
+ internals_1.state.completions.activity.set(seq, {
177
+ resolve,
178
+ reject,
179
+ });
183
180
  });
184
181
  }
185
182
  /**
@@ -241,16 +238,16 @@ async function scheduleLocalActivity(activityType, args, options) {
241
238
  }
242
239
  }
243
240
  exports.scheduleLocalActivity = scheduleLocalActivity;
244
- async function startChildWorkflowExecutionNextHandler({ options, headers, workflowType, seq, }) {
241
+ function startChildWorkflowExecutionNextHandler({ options, headers, workflowType, seq, }) {
245
242
  const workflowId = options.workflowId ?? uuid4();
246
243
  const startPromise = new Promise((resolve, reject) => {
247
244
  const scope = cancellation_scope_1.CancellationScope.current();
248
245
  if (scope.consideredCancelled) {
249
- scope.cancelRequested.catch(reject);
246
+ (0, stack_helpers_1.untrackPromise)(scope.cancelRequested.catch(reject));
250
247
  return;
251
248
  }
252
249
  if (scope.cancellable) {
253
- scope.cancelRequested.catch(() => {
250
+ (0, stack_helpers_1.untrackPromise)(scope.cancelRequested.catch(() => {
254
251
  const complete = !internals_1.state.completions.childWorkflowComplete.has(seq);
255
252
  const started = !internals_1.state.completions.childWorkflowStart.has(seq);
256
253
  if (started && !complete) {
@@ -270,12 +267,8 @@ async function startChildWorkflowExecutionNextHandler({ options, headers, workfl
270
267
  });
271
268
  }
272
269
  // Nothing to cancel otherwise
273
- });
270
+ }));
274
271
  }
275
- internals_1.state.completions.childWorkflowStart.set(seq, {
276
- resolve,
277
- reject,
278
- });
279
272
  internals_1.state.pushCommand({
280
273
  startChildWorkflowExecution: {
281
274
  seq,
@@ -299,43 +292,52 @@ async function startChildWorkflowExecutionNextHandler({ options, headers, workfl
299
292
  memo: options.memo && (0, common_1.mapToPayloads)(internals_1.state.payloadConverter, options.memo),
300
293
  },
301
294
  });
295
+ internals_1.state.completions.childWorkflowStart.set(seq, {
296
+ resolve,
297
+ reject,
298
+ });
302
299
  });
303
300
  // We construct a Promise for the completion of the child Workflow before we know
304
301
  // if the Workflow code will await it to capture the result in case it does.
305
302
  const completePromise = new Promise((resolve, reject) => {
306
303
  // Chain start Promise rejection to the complete Promise.
307
- startPromise.catch(reject);
304
+ (0, stack_helpers_1.untrackPromise)(startPromise.catch(reject));
308
305
  internals_1.state.completions.childWorkflowComplete.set(seq, {
309
306
  resolve,
310
307
  reject,
311
308
  });
312
309
  });
310
+ (0, stack_helpers_1.untrackPromise)(startPromise);
311
+ (0, stack_helpers_1.untrackPromise)(completePromise);
313
312
  // Prevent unhandled rejection because the completion might not be awaited
314
- completePromise.catch(() => undefined);
315
- return [startPromise, completePromise];
313
+ (0, stack_helpers_1.untrackPromise)(completePromise.catch(() => undefined));
314
+ const ret = new Promise((resolve) => resolve([startPromise, completePromise]));
315
+ (0, stack_helpers_1.untrackPromise)(ret);
316
+ return ret;
316
317
  }
317
- function signalWorkflowNextHandler({ seq, signalName, args, target }) {
318
+ function signalWorkflowNextHandler({ seq, signalName, args, target, headers }) {
318
319
  return new Promise((resolve, reject) => {
319
320
  if (internals_1.state.info === undefined) {
320
321
  throw new internal_workflow_common_1.IllegalStateError('Workflow uninitialized');
321
322
  }
322
323
  const scope = cancellation_scope_1.CancellationScope.current();
323
324
  if (scope.consideredCancelled) {
324
- scope.cancelRequested.catch(reject);
325
+ (0, stack_helpers_1.untrackPromise)(scope.cancelRequested.catch(reject));
325
326
  return;
326
327
  }
327
328
  if (scope.cancellable) {
328
- scope.cancelRequested.catch(() => {
329
+ (0, stack_helpers_1.untrackPromise)(scope.cancelRequested.catch(() => {
329
330
  if (!internals_1.state.completions.signalWorkflow.has(seq)) {
330
331
  return;
331
332
  }
332
333
  internals_1.state.pushCommand({ cancelSignalWorkflow: { seq } });
333
- });
334
+ }));
334
335
  }
335
336
  internals_1.state.pushCommand({
336
337
  signalExternalWorkflowExecution: {
337
338
  seq,
338
339
  args: (0, common_1.toPayloads)(internals_1.state.payloadConverter, ...args),
340
+ headers,
339
341
  signalName,
340
342
  ...(target.type === 'external'
341
343
  ? {
@@ -403,7 +405,7 @@ function proxyActivities(options) {
403
405
  if (typeof activityType !== 'string') {
404
406
  throw new TypeError(`Only strings are supported for Activity types, got: ${String(activityType)}`);
405
407
  }
406
- return (...args) => {
408
+ return function activityProxyFunction(...args) {
407
409
  return scheduleActivity(activityType, args, options);
408
410
  };
409
411
  },
@@ -435,13 +437,15 @@ function proxyLocalActivities(options) {
435
437
  if (typeof activityType !== 'string') {
436
438
  throw new TypeError(`Only strings are supported for Activity types, got: ${String(activityType)}`);
437
439
  }
438
- return (...args) => {
440
+ return function localActivityProxyFunction(...args) {
439
441
  return scheduleLocalActivity(activityType, args, options);
440
442
  };
441
443
  },
442
444
  });
443
445
  }
444
446
  exports.proxyLocalActivities = proxyLocalActivities;
447
+ // TODO: deprecate this patch after "enough" time has passed
448
+ const EXTERNAL_WF_CANCEL_PATCH = '__temporal_internal_connect_external_handle_cancel_to_scope';
445
449
  /**
446
450
  * Returns a client-side handle that can be used to signal and cancel an existing Workflow execution.
447
451
  * It takes a Workflow ID and optional run ID.
@@ -455,6 +459,26 @@ function getExternalWorkflowHandle(workflowId, runId) {
455
459
  if (internals_1.state.info === undefined) {
456
460
  throw new internal_workflow_common_1.IllegalStateError('Uninitialized workflow');
457
461
  }
462
+ // Connect this cancel operation to the current cancellation scope.
463
+ // This is behavior was introduced after v0.22.0 and is incompatible
464
+ // with histories generated with previous SDK versions and thus requires
465
+ // patching.
466
+ //
467
+ // We try to delay patching as much as possible to avoid polluting
468
+ // histories unless strictly required.
469
+ const scope = cancellation_scope_1.CancellationScope.current();
470
+ if (scope.cancellable) {
471
+ (0, stack_helpers_1.untrackPromise)(scope.cancelRequested.catch((err) => {
472
+ if (patched(EXTERNAL_WF_CANCEL_PATCH)) {
473
+ reject(err);
474
+ }
475
+ }));
476
+ }
477
+ if (scope.consideredCancelled) {
478
+ if (patched(EXTERNAL_WF_CANCEL_PATCH)) {
479
+ return;
480
+ }
481
+ }
458
482
  const seq = internals_1.state.nextSeqs.cancelWorkflow++;
459
483
  internals_1.state.pushCommand({
460
484
  requestCancelExternalWorkflowExecution: {
@@ -469,7 +493,7 @@ function getExternalWorkflowHandle(workflowId, runId) {
469
493
  internals_1.state.completions.cancelWorkflow.set(seq, { resolve, reject });
470
494
  });
471
495
  },
472
- async signal(def, ...args) {
496
+ signal(def, ...args) {
473
497
  return (0, internal_workflow_common_1.composeInterceptors)(internals_1.state.interceptors.outbound, 'signalWorkflow', signalWorkflowNextHandler)({
474
498
  seq: internals_1.state.nextSeqs.signalWorkflow++,
475
499
  signalName: typeof def === 'string' ? def : def.name,
@@ -478,6 +502,7 @@ function getExternalWorkflowHandle(workflowId, runId) {
478
502
  type: 'external',
479
503
  workflowExecution: { workflowId, runId },
480
504
  },
505
+ headers: {},
481
506
  });
482
507
  },
483
508
  };
@@ -493,20 +518,14 @@ async function startChild(workflowTypeOrFunc, options) {
493
518
  headers: {},
494
519
  workflowType,
495
520
  });
496
- const originalRunId = await started;
521
+ const firstExecutionRunId = await started;
497
522
  return {
498
523
  workflowId: optionsWithDefaults.workflowId,
499
- originalRunId,
500
- result() {
501
- if (completed === undefined) {
502
- throw new internal_workflow_common_1.IllegalStateError('Child Workflow was not started');
503
- }
504
- return completed;
524
+ firstExecutionRunId,
525
+ async result() {
526
+ return (await completed);
505
527
  },
506
528
  async signal(def, ...args) {
507
- if (started === undefined) {
508
- throw new internal_workflow_common_1.IllegalStateError('Workflow execution not started');
509
- }
510
529
  return (0, internal_workflow_common_1.composeInterceptors)(internals_1.state.interceptors.outbound, 'signalWorkflow', signalWorkflowNextHandler)({
511
530
  seq: internals_1.state.nextSeqs.signalWorkflow++,
512
531
  signalName: typeof def === 'string' ? def : def.name,
@@ -515,6 +534,7 @@ async function startChild(workflowTypeOrFunc, options) {
515
534
  type: 'child',
516
535
  childWorkflowId: optionsWithDefaults.workflowId,
517
536
  },
537
+ headers: {},
518
538
  });
519
539
  },
520
540
  };
@@ -524,13 +544,16 @@ async function executeChild(workflowTypeOrFunc, options) {
524
544
  const optionsWithDefaults = addDefaultWorkflowOptions(options ?? {});
525
545
  const workflowType = typeof workflowTypeOrFunc === 'string' ? workflowTypeOrFunc : workflowTypeOrFunc.name;
526
546
  const execute = (0, internal_workflow_common_1.composeInterceptors)(internals_1.state.interceptors.outbound, 'startChildWorkflowExecution', startChildWorkflowExecutionNextHandler);
527
- const [_started, completed] = await execute({
547
+ const execPromise = execute({
528
548
  seq: internals_1.state.nextSeqs.childWorkflow++,
529
549
  options: optionsWithDefaults,
530
550
  headers: {},
531
551
  workflowType,
532
552
  });
533
- return (await completed);
553
+ (0, stack_helpers_1.untrackPromise)(execPromise);
554
+ const completedPromise = execPromise.then(([_started, completed]) => completed);
555
+ (0, stack_helpers_1.untrackPromise)(completedPromise);
556
+ return completedPromise;
534
557
  }
535
558
  exports.executeChild = executeChild;
536
559
  /**
@@ -543,6 +566,26 @@ function workflowInfo() {
543
566
  return internals_1.state.info;
544
567
  }
545
568
  exports.workflowInfo = workflowInfo;
569
+ /**
570
+ * Returns whether or not code is executing in workflow context
571
+ */
572
+ function inWorkflowContext() {
573
+ try {
574
+ workflowInfo();
575
+ return true;
576
+ }
577
+ catch (err) {
578
+ // Use string comparison in case multiple versions of @temporalio/common are
579
+ // installed in which case an instanceof check would fail.
580
+ if (err.name === 'IllegalStateError') {
581
+ return false;
582
+ }
583
+ else {
584
+ throw err;
585
+ }
586
+ }
587
+ }
588
+ exports.inWorkflowContext = inWorkflowContext;
546
589
  /**
547
590
  * Get a reference to Sinks for exporting data out of the Workflow.
548
591
  *
@@ -615,7 +658,9 @@ function makeContinueAsNewFunc(options) {
615
658
  headers,
616
659
  taskQueue: options.taskQueue,
617
660
  memo: options.memo,
618
- searchAttributes: options.searchAttributes,
661
+ searchAttributes: options.searchAttributes
662
+ ? (0, common_1.mapToPayloads)(common_1.searchAttributePayloadConverter, options.searchAttributes)
663
+ : undefined,
619
664
  workflowRunTimeout: (0, internal_workflow_common_1.msOptionalToTs)(options.workflowRunTimeout),
620
665
  workflowTaskTimeout: (0, internal_workflow_common_1.msOptionalToTs)(options.workflowTaskTimeout),
621
666
  });
@@ -679,7 +724,7 @@ exports.uuid4 = uuid4;
679
724
  /**
680
725
  * Patch or upgrade workflow code by checking or stating that this workflow has a certain patch.
681
726
  *
682
- * See [docs page](https://docs.temporal.io/docs/typescript/versioning) for info.
727
+ * See [docs page](https://docs.temporal.io/typescript/versioning) for info.
683
728
  *
684
729
  * If the workflow is replaying an existing history, then this function returns true if that
685
730
  * history was produced by a worker which also had a `patched` call with the same `patchId`.
@@ -700,7 +745,7 @@ exports.patched = patched;
700
745
  /**
701
746
  * Indicate that a patch is being phased out.
702
747
  *
703
- * See [docs page](https://docs.temporal.io/docs/typescript/versioning) for info.
748
+ * See [docs page](https://docs.temporal.io/typescript/versioning) for info.
704
749
  *
705
750
  * Workflows with this call may be deployed alongside workflows with a {@link patched} call, but
706
751
  * they must *not* be deployed while any workers still exist running old code without a
@@ -721,11 +766,10 @@ exports.deprecatePatch = deprecatePatch;
721
766
  function patchInternal(patchId, deprecated) {
722
767
  // Patch operation does not support interception at the moment, if it did,
723
768
  // this would be the place to start the interception chain
724
- const { isReplaying } = workflowInfo();
725
769
  if (internals_1.state.workflow === undefined) {
726
770
  throw new internal_workflow_common_1.IllegalStateError('Patches cannot be used before Workflow starts');
727
771
  }
728
- const usePatch = !isReplaying || internals_1.state.knownPresentPatches.has(patchId);
772
+ const usePatch = !internals_1.state.isReplaying || internals_1.state.knownPresentPatches.has(patchId);
729
773
  // Avoid sending commands for patches core already knows about.
730
774
  // This optimization enables development of automatic patching tools.
731
775
  if (usePatch && !internals_1.state.sentPatches.has(patchId)) {
@@ -738,7 +782,7 @@ function patchInternal(patchId, deprecated) {
738
782
  }
739
783
  async function condition(fn, timeout) {
740
784
  if (timeout) {
741
- return await cancellation_scope_1.CancellationScope.cancellable(async () => {
785
+ return cancellation_scope_1.CancellationScope.cancellable(async () => {
742
786
  try {
743
787
  return await Promise.race([sleep(timeout).then(() => false), conditionInner(fn).then(() => true)]);
744
788
  }
@@ -754,15 +798,15 @@ function conditionInner(fn) {
754
798
  return new Promise((resolve, reject) => {
755
799
  const scope = cancellation_scope_1.CancellationScope.current();
756
800
  if (scope.consideredCancelled) {
757
- scope.cancelRequested.catch(reject);
801
+ (0, stack_helpers_1.untrackPromise)(scope.cancelRequested.catch(reject));
758
802
  return;
759
803
  }
760
804
  const seq = internals_1.state.nextSeqs.condition++;
761
805
  if (scope.cancellable) {
762
- scope.cancelRequested.catch((err) => {
806
+ (0, stack_helpers_1.untrackPromise)(scope.cancelRequested.catch((err) => {
763
807
  internals_1.state.blockedConditions.delete(seq);
764
808
  reject(err);
765
- });
809
+ }));
766
810
  }
767
811
  // Eager evaluation
768
812
  if (fn()) {
@@ -804,17 +848,17 @@ exports.defineQuery = defineQuery;
804
848
  * If this function is called multiple times for a given signal or query name the last handler will overwrite any previous calls.
805
849
  *
806
850
  * @param def a {@link SignalDefinition} or {@link QueryDefinition} as returned by {@link defineSignal} or {@link defineQuery} respectively.
807
- * @param handler a compatible handler function for the given definition.
851
+ * @param handler a compatible handler function for the given definition or `undefined` to unset the handler.
808
852
  */
809
853
  function setHandler(def, handler) {
810
854
  if (def.type === 'signal') {
811
855
  internals_1.state.signalHandlers.set(def.name, handler);
812
856
  const bufferedSignals = internals_1.state.bufferedSignals.get(def.name);
813
- if (bufferedSignals !== undefined) {
857
+ if (bufferedSignals !== undefined && handler !== undefined) {
858
+ internals_1.state.bufferedSignals.delete(def.name);
814
859
  for (const signal of bufferedSignals) {
815
860
  internals_1.state.activator.signalWorkflow(signal);
816
861
  }
817
- internals_1.state.bufferedSignals.delete(def.name);
818
862
  }
819
863
  }
820
864
  else if (def.type === 'query') {
@@ -825,4 +869,68 @@ function setHandler(def, handler) {
825
869
  }
826
870
  }
827
871
  exports.setHandler = setHandler;
872
+ /**
873
+ * Updates this Workflow's Search Attributes by merging the provided `searchAttributes` with the existing Search
874
+ * Attributes, `workflowInfo().searchAttributes`.
875
+ *
876
+ * For example, this Workflow code:
877
+ *
878
+ * ```ts
879
+ * upsertSearchAttributes({
880
+ * CustomIntField: [1, 2, 3],
881
+ * CustomBoolField: [true]
882
+ * });
883
+ * upsertSearchAttributes({
884
+ * CustomIntField: [42],
885
+ * CustomKeywordField: ['durable code', 'is great']
886
+ * });
887
+ * ```
888
+ *
889
+ * would result in the Workflow having these Search Attributes:
890
+ *
891
+ * ```ts
892
+ * {
893
+ * CustomIntField: [42],
894
+ * CustomBoolField: [true],
895
+ * CustomKeywordField: ['durable code', 'is great']
896
+ * }
897
+ * ```
898
+ *
899
+ * @param searchAttributes The Record to merge. Use a value of `[]` to clear a Search Attribute.
900
+ */
901
+ function upsertSearchAttributes(searchAttributes) {
902
+ if (!internals_1.state.info) {
903
+ throw new internal_workflow_common_1.IllegalStateError('`state.info` should be defined');
904
+ }
905
+ const mergedSearchAttributes = { ...internals_1.state.info.searchAttributes, ...searchAttributes };
906
+ if (!mergedSearchAttributes) {
907
+ throw new Error('searchAttributes must be a non-null SearchAttributes');
908
+ }
909
+ internals_1.state.pushCommand({
910
+ upsertWorkflowSearchAttributes: {
911
+ searchAttributes: (0, common_1.mapToPayloads)(common_1.searchAttributePayloadConverter, searchAttributes),
912
+ },
913
+ });
914
+ internals_1.state.info.searchAttributes = mergedSearchAttributes;
915
+ }
916
+ exports.upsertSearchAttributes = upsertSearchAttributes;
917
+ /**
918
+ * Get information about the currently executing Workflow Task.
919
+ *
920
+ * See {@link TaskInfo}
921
+ */
922
+ function taskInfo() {
923
+ const { isReplaying, historyLength } = internals_1.state;
924
+ if (isReplaying == null || historyLength == null) {
925
+ throw new internal_workflow_common_1.IllegalStateError('Workflow uninitialized');
926
+ }
927
+ return {
928
+ historyLength,
929
+ unsafe: {
930
+ isReplaying,
931
+ },
932
+ };
933
+ }
934
+ exports.taskInfo = taskInfo;
935
+ exports.stackTraceQuery = defineQuery('__stack_trace');
828
936
  //# sourceMappingURL=workflow.js.map