@mastra/temporal 0.6.0-alpha.0 → 0.6.0-alpha.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/dist/index.cjs +44 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +45 -4
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/temporal-workflow-runtime.mjs +116 -17
- package/dist/transforms/temporal-workflow-runtime.d.mts +2 -2
- package/dist/transforms/temporal-workflow-runtime.d.mts.map +1 -1
- package/dist/worker.cjs +44 -4
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.js +45 -5
- package/dist/worker.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ApplicationFailure,
|
|
3
|
+
TemporalFailure,
|
|
2
4
|
condition,
|
|
3
5
|
defineQuery,
|
|
4
6
|
defineSignal,
|
|
5
7
|
executeChild,
|
|
8
|
+
isCancellation,
|
|
6
9
|
proxyActivities,
|
|
7
10
|
log,
|
|
8
11
|
setHandler,
|
|
@@ -12,6 +15,39 @@ import {
|
|
|
12
15
|
const resumeSignal = defineSignal('mastra.resume');
|
|
13
16
|
const statusQuery = defineQuery('mastra.status');
|
|
14
17
|
|
|
18
|
+
// The step error an Activity or child Workflow put in its failure details. Failures without them
|
|
19
|
+
// (timeouts, Activities from an older Worker) give the innermost cause's type and message.
|
|
20
|
+
function readStepFailure(error) {
|
|
21
|
+
let current = error;
|
|
22
|
+
while (current) {
|
|
23
|
+
const details = current.details?.[0];
|
|
24
|
+
|
|
25
|
+
if (details?.error) {
|
|
26
|
+
return { error: details.error, ...(details.tripwire && { tripwire: details.tripwire }) };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!current.cause) {
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
current = current.cause;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return { error: { name: current?.type ?? current?.name ?? 'Error', message: current?.message ?? String(error) } };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Records a step as failed, in the core StepResult shape, before its failure ends the run.
|
|
40
|
+
function recordFailure(stepResults, id, payload, startedAt, error) {
|
|
41
|
+
stepResults[id] = {
|
|
42
|
+
__temporalStepResult: true,
|
|
43
|
+
status: 'failed',
|
|
44
|
+
payload,
|
|
45
|
+
...readStepFailure(error),
|
|
46
|
+
startedAt,
|
|
47
|
+
endedAt: Date.now(),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
15
51
|
export class TemporalExecutionEngine {
|
|
16
52
|
startToCloseTimeout;
|
|
17
53
|
activityHandle;
|
|
@@ -56,8 +92,26 @@ export class TemporalExecutionEngine {
|
|
|
56
92
|
steps: stepResults,
|
|
57
93
|
};
|
|
58
94
|
|
|
59
|
-
|
|
60
|
-
|
|
95
|
+
try {
|
|
96
|
+
for (const entry of params.graph.steps) {
|
|
97
|
+
result = await this.executeEntry(entry, result, stepResults);
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
// An error that isn't a Temporal failure comes from Workflow code. It fails only the Workflow
|
|
101
|
+
// Task, which Temporal retries, so a fixed Worker can still finish the run.
|
|
102
|
+
if (isCancellation(error) || !(error instanceof TemporalFailure)) {
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// The run still fails in Temporal. The details let start() return step records and the
|
|
107
|
+
// step's own error, which a thrown ActivityFailure would lose.
|
|
108
|
+
const failure = readStepFailure(error);
|
|
109
|
+
throw ApplicationFailure.create({
|
|
110
|
+
message: failure.error.message,
|
|
111
|
+
type: failure.error.name,
|
|
112
|
+
cause: error,
|
|
113
|
+
details: [{ ...failure, steps: stepResults }],
|
|
114
|
+
});
|
|
61
115
|
}
|
|
62
116
|
|
|
63
117
|
this.currentResult = {
|
|
@@ -82,9 +136,17 @@ export class TemporalExecutionEngine {
|
|
|
82
136
|
}
|
|
83
137
|
|
|
84
138
|
// Record steps in the core StepResult shape ({ status, payload, output, startedAt, endedAt }).
|
|
85
|
-
async recordStep(stepResults, id, payload, run) {
|
|
139
|
+
async recordStep(stepResults, id, payload, run, group) {
|
|
86
140
|
const startedAt = Date.now();
|
|
87
|
-
|
|
141
|
+
const attempt = async resumeData => {
|
|
142
|
+
try {
|
|
143
|
+
return await run(resumeData);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
recordFailure(stepResults, id, payload, startedAt, error);
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
let output = await attempt();
|
|
88
150
|
let suspendPayload;
|
|
89
151
|
let resumeData;
|
|
90
152
|
|
|
@@ -114,7 +176,11 @@ export class TemporalExecutionEngine {
|
|
|
114
176
|
|
|
115
177
|
this.suspendedSteps.add(id);
|
|
116
178
|
try {
|
|
117
|
-
await condition(() => this.resumePayloads.has(id));
|
|
179
|
+
await condition(() => this.resumePayloads.has(id) || group?.failed === true);
|
|
180
|
+
// A failed sibling branch ends the run, so the step stays suspended in the records.
|
|
181
|
+
if (!this.resumePayloads.has(id)) {
|
|
182
|
+
return output;
|
|
183
|
+
}
|
|
118
184
|
resumeData = this.resumePayloads.get(id)?.resumeData;
|
|
119
185
|
this.resumePayloads.delete(id);
|
|
120
186
|
} finally {
|
|
@@ -131,7 +197,7 @@ export class TemporalExecutionEngine {
|
|
|
131
197
|
state: this.initialState,
|
|
132
198
|
steps: stepResults,
|
|
133
199
|
};
|
|
134
|
-
output = await
|
|
200
|
+
output = await attempt(resumeData);
|
|
135
201
|
}
|
|
136
202
|
|
|
137
203
|
stepResults[id] = {
|
|
@@ -157,17 +223,22 @@ export class TemporalExecutionEngine {
|
|
|
157
223
|
return { inputData, ...extra, ...this.executionContext };
|
|
158
224
|
}
|
|
159
225
|
|
|
160
|
-
async executeEntry(entry, inputData, stepResults) {
|
|
226
|
+
async executeEntry(entry, inputData, stepResults, group) {
|
|
161
227
|
switch (entry.type) {
|
|
162
228
|
case 'step': {
|
|
163
229
|
log.info('step', { stepId: entry.step.id });
|
|
164
|
-
return this.recordStep(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
230
|
+
return this.recordStep(
|
|
231
|
+
stepResults,
|
|
232
|
+
entry.step.id,
|
|
233
|
+
inputData,
|
|
234
|
+
resumeData =>
|
|
235
|
+
this.activityHandle[entry.step.id](
|
|
236
|
+
this.activityParams(inputData, {
|
|
237
|
+
initData: this.initData,
|
|
238
|
+
...(resumeData !== undefined && { resumeData }),
|
|
239
|
+
}),
|
|
240
|
+
),
|
|
241
|
+
group,
|
|
171
242
|
);
|
|
172
243
|
}
|
|
173
244
|
|
|
@@ -213,11 +284,34 @@ export class TemporalExecutionEngine {
|
|
|
213
284
|
const entryId = parallelEntry =>
|
|
214
285
|
parallelEntry.type === 'childWorkflow' ? parallelEntry.workflowType : parallelEntry.step.id;
|
|
215
286
|
log.info('parallel', { steps: entry.steps.map(entryId) });
|
|
216
|
-
|
|
287
|
+
// Waits for every branch, as a local run does, so a failed run keeps the others' records.
|
|
288
|
+
// A failed branch also ends the wait of a suspended one, which would otherwise keep the run
|
|
289
|
+
// open until a resume. The flag is per group so a later parallel starts clean.
|
|
290
|
+
const group = { failed: false };
|
|
291
|
+
const settled = await Promise.allSettled(
|
|
292
|
+
entry.steps.map(step =>
|
|
293
|
+
this.executeEntry(step, inputData, stepResults, group).catch(error => {
|
|
294
|
+
group.failed = true;
|
|
295
|
+
throw error;
|
|
296
|
+
}),
|
|
297
|
+
),
|
|
298
|
+
);
|
|
299
|
+
const failures = settled.filter(outcome => outcome.status === 'rejected');
|
|
300
|
+
// Same order as in execute(), whatever the branch order: a cancelled run ends Canceled, and
|
|
301
|
+
// an error from Workflow code wins so Temporal retries the Workflow Task.
|
|
302
|
+
const rejected =
|
|
303
|
+
failures.find(outcome => isCancellation(outcome.reason)) ??
|
|
304
|
+
failures.find(outcome => !(outcome.reason instanceof TemporalFailure)) ??
|
|
305
|
+
failures[0];
|
|
306
|
+
|
|
307
|
+
if (rejected) {
|
|
308
|
+
throw rejected.reason;
|
|
309
|
+
}
|
|
310
|
+
|
|
217
311
|
const out = {};
|
|
218
312
|
|
|
219
313
|
entry.steps.forEach((step, i) => {
|
|
220
|
-
out[entryId(step)] =
|
|
314
|
+
out[entryId(step)] = settled[i].value;
|
|
221
315
|
});
|
|
222
316
|
|
|
223
317
|
return out;
|
|
@@ -301,7 +395,12 @@ export class TemporalExecutionEngine {
|
|
|
301
395
|
}
|
|
302
396
|
});
|
|
303
397
|
|
|
304
|
-
|
|
398
|
+
try {
|
|
399
|
+
await Promise.all(workers);
|
|
400
|
+
} catch (error) {
|
|
401
|
+
recordFailure(stepResults, entry.step.id, inputData, startedAt, error);
|
|
402
|
+
throw error;
|
|
403
|
+
}
|
|
305
404
|
stepResults[entry.step.id] = {
|
|
306
405
|
__temporalStepResult: true,
|
|
307
406
|
status: 'success',
|
|
@@ -17,7 +17,7 @@ export declare class TemporalExecutionEngine {
|
|
|
17
17
|
constructor(params: any);
|
|
18
18
|
execute(params: any): Promise<any>;
|
|
19
19
|
resumeTarget(payload: any): any;
|
|
20
|
-
recordStep(stepResults: any, id: any, payload: any, run: any): Promise<any>;
|
|
20
|
+
recordStep(stepResults: any, id: any, payload: any, run: any, group: any): Promise<any>;
|
|
21
21
|
activityParams(inputData: any, extra?: {}): {
|
|
22
22
|
requestContext?: any;
|
|
23
23
|
runId?: any;
|
|
@@ -26,7 +26,7 @@ export declare class TemporalExecutionEngine {
|
|
|
26
26
|
workflowId?: any;
|
|
27
27
|
inputData: any;
|
|
28
28
|
};
|
|
29
|
-
executeEntry(entry: any, inputData: any, stepResults: any): Promise<any>;
|
|
29
|
+
executeEntry(entry: any, inputData: any, stepResults: any, group: any): Promise<any>;
|
|
30
30
|
}
|
|
31
31
|
export declare function createWorkflow(workflowId: any, options: any): ((startArgs: any) => Promise<any>) & {
|
|
32
32
|
then(stepId: any): (startArgs: any) => Promise<any>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temporal-workflow-runtime.d.mts","sourceRoot":"","sources":["../../src/transforms/temporal-workflow-runtime.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"temporal-workflow-runtime.d.mts","sourceRoot":"","sources":["../../src/transforms/temporal-workflow-runtime.mjs"],"names":[],"mappings":"AAkDA,qBAAa,uBAAuB;IA4B3B,gBAAgB;;;;;;;IA3BvB,mBAAmB,MAAC;IACpB,cAAc,wGAAC;IACf,cAAc,gBAAa;IAC3B,cAAc,WAAa;IAC3B,YAAY,gBAAa;IACzB,aAAa,MAAC;IACd,QAAQ,MAAC;IACT,YAAY,MAAC;IAEb,YAAY,MAAM,KAAA,EAajB;IAEK,OAAO,CAAC,MAAM,KAAA,gBAiDnB;IAED,YAAY,CAAC,OAAO,KAAA,OASnB;IAGK,UAAU,CAAC,WAAW,KAAA,EAAE,EAAE,KAAA,EAAE,OAAO,KAAA,EAAE,GAAG,KAAA,EAAE,KAAK,KAAA,gBAiFpD;IAED,cAAc,CAAC,SAAS,KAAA,EAAE,KAAK,KAAK;;;;;;;MAEnC;IAEK,YAAY,CAAC,KAAK,KAAA,EAAE,SAAS,KAAA,EAAE,WAAW,KAAA,EAAE,KAAK,KAAA,gBAgMtD;CACF;AAED,wBAAgB,cAAc,CAAC,UAAU,KAAA,EAAE,OAAO,KAAA;;;;;;;;;;;;EAuJjD"}
|
package/dist/worker.cjs
CHANGED
|
@@ -23,12 +23,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
const require_utils = require("./utils-BVE1AkTt.cjs");
|
|
24
24
|
let _mastra_core_workflows = require("@mastra/core/workflows");
|
|
25
25
|
let _mastra_core_di = require("@mastra/core/di");
|
|
26
|
+
let _mastra_core_error = require("@mastra/core/error");
|
|
26
27
|
let fs = require("fs");
|
|
27
28
|
let fs_promises = require("fs/promises");
|
|
28
29
|
let path = require("path");
|
|
29
30
|
path = __toESM(path, 1);
|
|
30
31
|
let url = require("url");
|
|
31
|
-
let _mastra_core_error = require("@mastra/core/error");
|
|
32
32
|
let _mastra_core_schema = require("@mastra/core/schema");
|
|
33
33
|
let _temporalio_activity = require("@temporalio/activity");
|
|
34
34
|
let _babel_generator = require("@babel/generator");
|
|
@@ -1212,6 +1212,44 @@ function getActivityBindingsPath(outputDir) {
|
|
|
1212
1212
|
return path.default.join(outputDir, ACTIVITY_BINDINGS_FILE_NAME);
|
|
1213
1213
|
}
|
|
1214
1214
|
/**
|
|
1215
|
+
* Temporal reduces a thrown error to its class name and message, so the step's own fields go in
|
|
1216
|
+
* the failure's `details` for start() to rebuild the result a local run returns.
|
|
1217
|
+
*/
|
|
1218
|
+
function toStepFailureDetails(error) {
|
|
1219
|
+
const serialized = (0, _mastra_core_error.getErrorFromUnknown)(error, { serializeStack: false }).toJSON();
|
|
1220
|
+
let details;
|
|
1221
|
+
try {
|
|
1222
|
+
details = JSON.parse(JSON.stringify(serialized));
|
|
1223
|
+
} catch {
|
|
1224
|
+
details = {
|
|
1225
|
+
name: serialized.name,
|
|
1226
|
+
message: serialized.message
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
if (error instanceof Error && error.name === "TripWire") {
|
|
1230
|
+
const { options, processorId } = error;
|
|
1231
|
+
return {
|
|
1232
|
+
error: details,
|
|
1233
|
+
tripwire: {
|
|
1234
|
+
reason: error.message,
|
|
1235
|
+
retry: options?.retry,
|
|
1236
|
+
metadata: options?.metadata,
|
|
1237
|
+
processorId
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
return { error: details };
|
|
1242
|
+
}
|
|
1243
|
+
/** Fails as Temporal's own conversion would (same type, message and retries), plus the details. */
|
|
1244
|
+
async function runStep(execute) {
|
|
1245
|
+
try {
|
|
1246
|
+
return await execute();
|
|
1247
|
+
} catch (error) {
|
|
1248
|
+
if (error instanceof _temporalio_activity.ApplicationFailure || error instanceof _temporalio_activity.CancelledFailure) throw error;
|
|
1249
|
+
throw _temporalio_activity.ApplicationFailure.fromError(error, { details: [toStepFailureDetails(error)] });
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1215
1253
|
* Activities queued by a Workflow built before `validateInputs` existed carry no flag and run
|
|
1216
1254
|
* unchecked. A mismatch is non-retryable because a retry gets the same input.
|
|
1217
1255
|
*/
|
|
@@ -1234,7 +1272,8 @@ async function checkStepParams(stepId, { inputSchema, requestContextSchema }, {
|
|
|
1234
1272
|
type: error instanceof _mastra_core_error.MastraError ? error.id : error.name,
|
|
1235
1273
|
message: error.message,
|
|
1236
1274
|
nonRetryable: true,
|
|
1237
|
-
cause: error
|
|
1275
|
+
cause: error,
|
|
1276
|
+
details: [toStepFailureDetails(error)]
|
|
1238
1277
|
});
|
|
1239
1278
|
return {
|
|
1240
1279
|
...params,
|
|
@@ -1298,9 +1337,10 @@ var MastraPlugin = class {
|
|
|
1298
1337
|
if (generatedActivities[binding.stepId]) continue;
|
|
1299
1338
|
generatedActivities[binding.stepId] = async (params) => {
|
|
1300
1339
|
const activity = (await this.#loadCompiledActivitiesModule(compiledActivitiesPath))[binding.exportName];
|
|
1301
|
-
if (typeof activity === "function") return activity(params);
|
|
1340
|
+
if (typeof activity === "function") return runStep(() => activity(params));
|
|
1302
1341
|
if (typeof activity?.execute !== "function") throw new Error(`Unable to load activity '${binding.exportName}' from ${compiledActivitiesPath}`);
|
|
1303
|
-
|
|
1342
|
+
const checkedParams = await checkStepParams(binding.stepId, activity, params);
|
|
1343
|
+
return runStep(() => activity.execute(checkedParams));
|
|
1304
1344
|
};
|
|
1305
1345
|
}
|
|
1306
1346
|
return generatedActivities;
|