@temporalio/workflow 1.10.3 → 1.11.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/flags.d.ts +18 -0
- package/lib/flags.js +19 -1
- package/lib/flags.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/interfaces.d.ts +35 -13
- package/lib/interfaces.js.map +1 -1
- package/lib/internals.d.ts +58 -20
- package/lib/internals.js +151 -83
- package/lib/internals.js.map +1 -1
- package/lib/update-scope.d.ts +43 -0
- package/lib/update-scope.js +41 -0
- package/lib/update-scope.js.map +1 -0
- package/lib/worker-interface.d.ts +1 -8
- package/lib/worker-interface.js +13 -58
- package/lib/worker-interface.js.map +1 -1
- package/lib/workflow.d.ts +67 -8
- package/lib/workflow.js +107 -12
- package/lib/workflow.js.map +1 -1
- package/package.json +4 -4
- package/src/flags.ts +20 -1
- package/src/index.ts +3 -3
- package/src/interfaces.ts +41 -14
- package/src/internals.ts +192 -88
- package/src/update-scope.ts +67 -0
- package/src/worker-interface.ts +15 -64
- package/src/workflow.ts +116 -11
package/src/worker-interface.ts
CHANGED
|
@@ -4,15 +4,14 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import { IllegalStateError } from '@temporalio/common';
|
|
7
|
-
import { tsToMs } from '@temporalio/common/lib/time';
|
|
8
7
|
import { composeInterceptors } from '@temporalio/common/lib/interceptors';
|
|
9
8
|
import { coresdk } from '@temporalio/proto';
|
|
10
9
|
import { disableStorage } from './cancellation-scope';
|
|
10
|
+
import { disableUpdateStorage } from './update-scope';
|
|
11
11
|
import { WorkflowInterceptorsFactory } from './interceptors';
|
|
12
12
|
import { WorkflowCreateOptionsInternal } from './interfaces';
|
|
13
13
|
import { Activator } from './internals';
|
|
14
14
|
import { setActivatorUntyped, getActivator } from './global-attributes';
|
|
15
|
-
import { type SinkCall } from './sinks';
|
|
16
15
|
|
|
17
16
|
// Export the type for use on the "worker" side
|
|
18
17
|
export { PromiseStackStore } from './internals';
|
|
@@ -33,7 +32,7 @@ export function initRuntime(options: WorkflowCreateOptionsInternal): void {
|
|
|
33
32
|
unsafe: { ...options.info.unsafe, now: OriginalDate.now },
|
|
34
33
|
}),
|
|
35
34
|
});
|
|
36
|
-
// There's
|
|
35
|
+
// There's one activator per workflow instance, set it globally on the context.
|
|
37
36
|
// We do this before importing any user code so user code can statically reference @temporalio/workflow functions
|
|
38
37
|
// as well as Date and Math.random.
|
|
39
38
|
setActivatorUntyped(activator);
|
|
@@ -110,66 +109,26 @@ function fixPrototypes<X>(obj: X): X {
|
|
|
110
109
|
|
|
111
110
|
/**
|
|
112
111
|
* Run a chunk of activation jobs
|
|
113
|
-
* @returns a boolean indicating whether job was processed or ignored
|
|
114
112
|
*/
|
|
115
|
-
export function activate(activation: coresdk.workflow_activation.
|
|
113
|
+
export function activate(activation: coresdk.workflow_activation.IWorkflowActivation, batchIndex: number): void {
|
|
116
114
|
const activator = getActivator();
|
|
117
|
-
const intercept = composeInterceptors(activator.interceptors.internals, 'activate', ({ activation
|
|
118
|
-
if (batchIndex === 0) {
|
|
119
|
-
if (!activation.jobs) {
|
|
120
|
-
throw new TypeError('Got activation with no jobs');
|
|
121
|
-
}
|
|
122
|
-
if (activation.timestamp != null) {
|
|
123
|
-
// timestamp will not be updated for activation that contain only queries
|
|
124
|
-
activator.now = tsToMs(activation.timestamp);
|
|
125
|
-
}
|
|
126
|
-
activator.addKnownFlags(activation.availableInternalFlags ?? []);
|
|
127
|
-
|
|
128
|
-
// The Rust Core ensures that these activation fields are not null
|
|
129
|
-
activator.mutateWorkflowInfo((info) => ({
|
|
130
|
-
...info,
|
|
131
|
-
historyLength: activation.historyLength as number,
|
|
132
|
-
// Exact truncation for multi-petabyte histories
|
|
133
|
-
// historySize === 0 means WFT was generated by pre-1.20.0 server, and the history size is unknown
|
|
134
|
-
historySize: activation.historySizeBytes?.toNumber() || 0,
|
|
135
|
-
continueAsNewSuggested: activation.continueAsNewSuggested ?? false,
|
|
136
|
-
currentBuildId: activation.buildIdForCurrentTask ?? undefined,
|
|
137
|
-
unsafe: {
|
|
138
|
-
...info.unsafe,
|
|
139
|
-
isReplaying: activation.isReplaying ?? false,
|
|
140
|
-
},
|
|
141
|
-
}));
|
|
142
|
-
}
|
|
143
|
-
|
|
115
|
+
const intercept = composeInterceptors(activator.interceptors.internals, 'activate', ({ activation }) => {
|
|
144
116
|
// Cast from the interface to the class which has the `variant` attribute.
|
|
145
117
|
// This is safe because we know that activation is a proto class.
|
|
146
118
|
const jobs = activation.jobs as coresdk.workflow_activation.WorkflowActivationJob[];
|
|
147
119
|
|
|
148
120
|
for (const job of jobs) {
|
|
149
|
-
if (job.variant === undefined)
|
|
150
|
-
throw new TypeError('Expected job.variant to be defined');
|
|
151
|
-
}
|
|
121
|
+
if (job.variant === undefined) throw new TypeError('Expected job.variant to be defined');
|
|
152
122
|
|
|
153
123
|
const variant = job[job.variant];
|
|
154
|
-
if (!variant) {
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
// The only job that can be executed on a completed workflow is a query.
|
|
158
|
-
// We might get other jobs after completion for instance when a single
|
|
159
|
-
// activation contains multiple jobs and the first one completes the workflow.
|
|
160
|
-
if (activator.completed && job.variant !== 'queryWorkflow') {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
124
|
+
if (!variant) throw new TypeError(`Expected job.${job.variant} to be set`);
|
|
125
|
+
|
|
163
126
|
activator[job.variant](variant as any /* TS can't infer this type */);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
127
|
+
|
|
128
|
+
if (job.variant !== 'queryWorkflow') tryUnblockConditions();
|
|
167
129
|
}
|
|
168
130
|
});
|
|
169
|
-
intercept({
|
|
170
|
-
activation,
|
|
171
|
-
batchIndex,
|
|
172
|
-
});
|
|
131
|
+
intercept({ activation, batchIndex });
|
|
173
132
|
}
|
|
174
133
|
|
|
175
134
|
/**
|
|
@@ -182,20 +141,18 @@ export function concludeActivation(): coresdk.workflow_completion.IWorkflowActiv
|
|
|
182
141
|
const activator = getActivator();
|
|
183
142
|
activator.rejectBufferedUpdates();
|
|
184
143
|
const intercept = composeInterceptors(activator.interceptors.internals, 'concludeActivation', (input) => input);
|
|
185
|
-
const { info } = activator;
|
|
186
144
|
const activationCompletion = activator.concludeActivation();
|
|
187
145
|
const { commands } = intercept({ commands: activationCompletion.commands });
|
|
146
|
+
if (activator.completed) {
|
|
147
|
+
activator.warnIfUnfinishedHandlers();
|
|
148
|
+
}
|
|
188
149
|
|
|
189
150
|
return {
|
|
190
|
-
runId: info.runId,
|
|
151
|
+
runId: activator.info.runId,
|
|
191
152
|
successful: { ...activationCompletion, commands },
|
|
192
153
|
};
|
|
193
154
|
}
|
|
194
155
|
|
|
195
|
-
export function getAndResetSinkCalls(): SinkCall[] {
|
|
196
|
-
return getActivator().getAndResetSinkCalls();
|
|
197
|
-
}
|
|
198
|
-
|
|
199
156
|
/**
|
|
200
157
|
* Loop through all blocked conditions, evaluate and unblock if possible.
|
|
201
158
|
*
|
|
@@ -220,16 +177,10 @@ export function tryUnblockConditions(): number {
|
|
|
220
177
|
return numUnblocked;
|
|
221
178
|
}
|
|
222
179
|
|
|
223
|
-
/**
|
|
224
|
-
* Predicate used to prevent triggering conditions for non-query and non-patch jobs.
|
|
225
|
-
*/
|
|
226
|
-
export function shouldUnblockConditions(job: coresdk.workflow_activation.IWorkflowActivationJob): boolean {
|
|
227
|
-
return !job.queryWorkflow && !job.notifyHasPatch;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
180
|
export function dispose(): void {
|
|
231
181
|
const dispose = composeInterceptors(getActivator().interceptors.internals, 'dispose', async () => {
|
|
232
182
|
disableStorage();
|
|
183
|
+
disableUpdateStorage();
|
|
233
184
|
});
|
|
234
185
|
dispose({});
|
|
235
186
|
}
|
package/src/workflow.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
ActivityOptions,
|
|
4
4
|
compileRetryPolicy,
|
|
5
5
|
extractWorkflowType,
|
|
6
|
+
HandlerUnfinishedPolicy,
|
|
6
7
|
LocalActivityOptions,
|
|
7
8
|
mapToPayloads,
|
|
8
9
|
QueryDefinition,
|
|
@@ -23,6 +24,7 @@ import { Duration, msOptionalToTs, msToNumber, msToTs, requiredTsToMs } from '@t
|
|
|
23
24
|
import { composeInterceptors } from '@temporalio/common/lib/interceptors';
|
|
24
25
|
import { temporal } from '@temporalio/proto';
|
|
25
26
|
import { CancellationScope, registerSleepImplementation } from './cancellation-scope';
|
|
27
|
+
import { UpdateScope } from './update-scope';
|
|
26
28
|
import {
|
|
27
29
|
ActivityInput,
|
|
28
30
|
LocalActivityInput,
|
|
@@ -43,6 +45,7 @@ import {
|
|
|
43
45
|
SignalHandlerOptions,
|
|
44
46
|
UpdateHandlerOptions,
|
|
45
47
|
WorkflowInfo,
|
|
48
|
+
UpdateInfo,
|
|
46
49
|
} from './interfaces';
|
|
47
50
|
import { LocalActivityDoBackoff } from './errors';
|
|
48
51
|
import { assertInWorkflowContext, getActivator, maybeGetActivator } from './global-attributes';
|
|
@@ -53,7 +56,7 @@ import { ChildWorkflowHandle, ExternalWorkflowHandle } from './workflow-handle';
|
|
|
53
56
|
registerSleepImplementation(sleep);
|
|
54
57
|
|
|
55
58
|
/**
|
|
56
|
-
* Adds default values
|
|
59
|
+
* Adds default values of `workflowId` and `cancellationType` to given workflow options.
|
|
57
60
|
*/
|
|
58
61
|
export function addDefaultWorkflowOptions<T extends Workflow>(
|
|
59
62
|
opts: WithWorkflowArgs<T, ChildWorkflowOptions>
|
|
@@ -867,6 +870,19 @@ export function workflowInfo(): WorkflowInfo {
|
|
|
867
870
|
return activator.info;
|
|
868
871
|
}
|
|
869
872
|
|
|
873
|
+
/**
|
|
874
|
+
* Get information about the current update if any.
|
|
875
|
+
*
|
|
876
|
+
* @return Info for the current update handler the code calling this is executing
|
|
877
|
+
* within if any.
|
|
878
|
+
*
|
|
879
|
+
* @experimental
|
|
880
|
+
*/
|
|
881
|
+
export function currentUpdateInfo(): UpdateInfo | undefined {
|
|
882
|
+
assertInWorkflowContext('Workflow.currentUpdateInfo(...) may only be used from a Workflow Execution.');
|
|
883
|
+
return UpdateScope.current();
|
|
884
|
+
}
|
|
885
|
+
|
|
870
886
|
/**
|
|
871
887
|
* Returns whether or not code is executing in workflow context
|
|
872
888
|
*/
|
|
@@ -1080,8 +1096,8 @@ function conditionInner(fn: () => boolean): Promise<void> {
|
|
|
1080
1096
|
/**
|
|
1081
1097
|
* Define an update method for a Workflow.
|
|
1082
1098
|
*
|
|
1083
|
-
*
|
|
1084
|
-
*
|
|
1099
|
+
* A definition is used to register a handler in the Workflow via {@link setHandler} and to update a Workflow using a {@link WorkflowHandle}, {@link ChildWorkflowHandle} or {@link ExternalWorkflowHandle}.
|
|
1100
|
+
* A definition can be reused in multiple Workflows.
|
|
1085
1101
|
*/
|
|
1086
1102
|
export function defineUpdate<Ret, Args extends any[] = [], Name extends string = string>(
|
|
1087
1103
|
name: Name
|
|
@@ -1095,8 +1111,8 @@ export function defineUpdate<Ret, Args extends any[] = [], Name extends string =
|
|
|
1095
1111
|
/**
|
|
1096
1112
|
* Define a signal method for a Workflow.
|
|
1097
1113
|
*
|
|
1098
|
-
*
|
|
1099
|
-
*
|
|
1114
|
+
* A definition is used to register a handler in the Workflow via {@link setHandler} and to signal a Workflow using a {@link WorkflowHandle}, {@link ChildWorkflowHandle} or {@link ExternalWorkflowHandle}.
|
|
1115
|
+
* A definition can be reused in multiple Workflows.
|
|
1100
1116
|
*/
|
|
1101
1117
|
export function defineSignal<Args extends any[] = [], Name extends string = string>(
|
|
1102
1118
|
name: Name
|
|
@@ -1110,8 +1126,8 @@ export function defineSignal<Args extends any[] = [], Name extends string = stri
|
|
|
1110
1126
|
/**
|
|
1111
1127
|
* Define a query method for a Workflow.
|
|
1112
1128
|
*
|
|
1113
|
-
*
|
|
1114
|
-
*
|
|
1129
|
+
* A definition is used to register a handler in the Workflow via {@link setHandler} and to query a Workflow using a {@link WorkflowHandle}.
|
|
1130
|
+
* A definition can be reused in multiple Workflows.
|
|
1115
1131
|
*/
|
|
1116
1132
|
export function defineQuery<Ret, Args extends any[] = [], Name extends string = string>(
|
|
1117
1133
|
name: Name
|
|
@@ -1178,7 +1194,7 @@ export function setHandler<Ret, Args extends any[], T extends UpdateDefinition<R
|
|
|
1178
1194
|
// 1. sdk-core sorts Signal and Update jobs (and Patches) ahead of all other
|
|
1179
1195
|
// jobs. Thus if the handler is available at the start of the Activation then
|
|
1180
1196
|
// the Signal/Update will be executed before Workflow code is executed. If it
|
|
1181
|
-
// is not, then the Signal/Update calls
|
|
1197
|
+
// is not, then the Signal/Update calls are pushed to a buffer.
|
|
1182
1198
|
//
|
|
1183
1199
|
// 2. On each call to setHandler for a given Signal/Update, we make a pass
|
|
1184
1200
|
// through the buffer list. If a buffered job is associated with the just-set
|
|
@@ -1226,7 +1242,7 @@ export function setHandler<Ret, Args extends any[], T extends UpdateDefinition<R
|
|
|
1226
1242
|
// Worker polling until after they have verified that both requests have
|
|
1227
1243
|
// succeeded.)
|
|
1228
1244
|
//
|
|
1229
|
-
//
|
|
1245
|
+
// 4. If an Update has a validation function then it is executed immediately
|
|
1230
1246
|
// prior to the handler. (Note that the validation function is required to be
|
|
1231
1247
|
// synchronous).
|
|
1232
1248
|
export function setHandler<
|
|
@@ -1243,8 +1259,10 @@ export function setHandler<
|
|
|
1243
1259
|
if (def.type === 'update') {
|
|
1244
1260
|
if (typeof handler === 'function') {
|
|
1245
1261
|
const updateOptions = options as UpdateHandlerOptions<Args> | undefined;
|
|
1262
|
+
|
|
1246
1263
|
const validator = updateOptions?.validator as WorkflowUpdateValidatorType | undefined;
|
|
1247
|
-
|
|
1264
|
+
const unfinishedPolicy = updateOptions?.unfinishedPolicy ?? HandlerUnfinishedPolicy.WARN_AND_ABANDON;
|
|
1265
|
+
activator.updateHandlers.set(def.name, { handler, validator, description, unfinishedPolicy });
|
|
1248
1266
|
activator.dispatchBufferedUpdates();
|
|
1249
1267
|
} else if (handler == null) {
|
|
1250
1268
|
activator.updateHandlers.delete(def.name);
|
|
@@ -1253,7 +1271,9 @@ export function setHandler<
|
|
|
1253
1271
|
}
|
|
1254
1272
|
} else if (def.type === 'signal') {
|
|
1255
1273
|
if (typeof handler === 'function') {
|
|
1256
|
-
|
|
1274
|
+
const signalOptions = options as SignalHandlerOptions | undefined;
|
|
1275
|
+
const unfinishedPolicy = signalOptions?.unfinishedPolicy ?? HandlerUnfinishedPolicy.WARN_AND_ABANDON;
|
|
1276
|
+
activator.signalHandlers.set(def.name, { handler: handler as any, description, unfinishedPolicy });
|
|
1257
1277
|
activator.dispatchBufferedSignals();
|
|
1258
1278
|
} else if (handler == null) {
|
|
1259
1279
|
activator.signalHandlers.delete(def.name);
|
|
@@ -1351,6 +1371,91 @@ export function upsertSearchAttributes(searchAttributes: SearchAttributes): void
|
|
|
1351
1371
|
});
|
|
1352
1372
|
}
|
|
1353
1373
|
|
|
1374
|
+
/**
|
|
1375
|
+
* Updates this Workflow's Memos by merging the provided `memo` with existing
|
|
1376
|
+
* Memos (as returned by `workflowInfo().memo`).
|
|
1377
|
+
*
|
|
1378
|
+
* New memo is merged by replacing properties of the same name _at the first
|
|
1379
|
+
* level only_. Setting a property to value `undefined` or `null` clears that
|
|
1380
|
+
* key from the Memo.
|
|
1381
|
+
*
|
|
1382
|
+
* For example:
|
|
1383
|
+
*
|
|
1384
|
+
* ```ts
|
|
1385
|
+
* upsertMemo({
|
|
1386
|
+
* key1: value,
|
|
1387
|
+
* key3: { subkey1: value }
|
|
1388
|
+
* key4: value,
|
|
1389
|
+
* });
|
|
1390
|
+
* upsertMemo({
|
|
1391
|
+
* key2: value
|
|
1392
|
+
* key3: { subkey2: value }
|
|
1393
|
+
* key4: undefined,
|
|
1394
|
+
* });
|
|
1395
|
+
* ```
|
|
1396
|
+
*
|
|
1397
|
+
* would result in the Workflow having these Memo:
|
|
1398
|
+
*
|
|
1399
|
+
* ```ts
|
|
1400
|
+
* {
|
|
1401
|
+
* key1: value,
|
|
1402
|
+
* key2: value,
|
|
1403
|
+
* key3: { subkey2: value } // Note this object was completely replaced
|
|
1404
|
+
* // Note that key4 was completely removed
|
|
1405
|
+
* }
|
|
1406
|
+
* ```
|
|
1407
|
+
*
|
|
1408
|
+
* @param memo The Record to merge.
|
|
1409
|
+
*/
|
|
1410
|
+
export function upsertMemo(memo: Record<string, unknown>): void {
|
|
1411
|
+
const activator = assertInWorkflowContext('Workflow.upsertMemo(...) may only be used from a Workflow Execution.');
|
|
1412
|
+
|
|
1413
|
+
if (memo == null) {
|
|
1414
|
+
throw new Error('memo must be a non-null Record');
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
activator.pushCommand({
|
|
1418
|
+
modifyWorkflowProperties: {
|
|
1419
|
+
upsertedMemo: {
|
|
1420
|
+
fields: mapToPayloads(
|
|
1421
|
+
activator.payloadConverter,
|
|
1422
|
+
// Convert null to undefined
|
|
1423
|
+
Object.fromEntries(Object.entries(memo).map(([k, v]) => [k, v ?? undefined]))
|
|
1424
|
+
),
|
|
1425
|
+
},
|
|
1426
|
+
},
|
|
1427
|
+
});
|
|
1428
|
+
|
|
1429
|
+
activator.mutateWorkflowInfo((info: WorkflowInfo): WorkflowInfo => {
|
|
1430
|
+
return {
|
|
1431
|
+
...info,
|
|
1432
|
+
memo: Object.fromEntries(
|
|
1433
|
+
Object.entries({
|
|
1434
|
+
...info.memo,
|
|
1435
|
+
...memo,
|
|
1436
|
+
}).filter(([_, v]) => v != null)
|
|
1437
|
+
),
|
|
1438
|
+
};
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
/**
|
|
1443
|
+
* Whether update and signal handlers have finished executing.
|
|
1444
|
+
*
|
|
1445
|
+
* Consider waiting on this condition before workflow return or continue-as-new, to prevent
|
|
1446
|
+
* interruption of in-progress handlers by workflow exit:
|
|
1447
|
+
*
|
|
1448
|
+
* ```ts
|
|
1449
|
+
* await workflow.condition(workflow.allHandlersFinished)
|
|
1450
|
+
* ```
|
|
1451
|
+
*
|
|
1452
|
+
* @returns true if there are no in-progress update or signal handler executions.
|
|
1453
|
+
*/
|
|
1454
|
+
export function allHandlersFinished(): boolean {
|
|
1455
|
+
const activator = assertInWorkflowContext('allHandlersFinished() may only be used from a Workflow Execution.');
|
|
1456
|
+
return activator.inProgressSignals.size === 0 && activator.inProgressUpdates.size === 0;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1354
1459
|
export const stackTraceQuery = defineQuery<string>('__stack_trace');
|
|
1355
1460
|
export const enhancedStackTraceQuery = defineQuery<EnhancedStackTrace>('__enhanced_stack_trace');
|
|
1356
1461
|
export const workflowMetadataQuery = defineQuery<temporal.api.sdk.v1.IWorkflowMetadata>('__temporal_workflow_metadata');
|