@mastra/client-js 0.0.0-message-ordering-20250415215612 → 0.0.0-message-list-update-20250715150321

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.
@@ -1,5 +1,16 @@
1
- import type { GetWorkflowResponse, ClientOptions, WorkflowRunResult } from '../types';
1
+ import type { RuntimeContext } from '@mastra/core/runtime-context';
2
+ import type {
3
+ ClientOptions,
4
+ GetWorkflowResponse,
5
+ GetWorkflowRunsResponse,
6
+ GetWorkflowRunsParams,
7
+ WorkflowRunResult,
8
+ WorkflowWatchResult,
9
+ GetWorkflowRunByIdResponse,
10
+ GetWorkflowRunExecutionResultResponse,
11
+ } from '../types';
2
12
 
13
+ import { parseClientRuntimeContext } from '../utils';
3
14
  import { BaseResource } from './base';
4
15
 
5
16
  const RECORD_SEPARATOR = '\x1E';
@@ -12,6 +23,77 @@ export class Workflow extends BaseResource {
12
23
  super(options);
13
24
  }
14
25
 
26
+ /**
27
+ * Creates an async generator that processes a readable stream and yields workflow records
28
+ * separated by the Record Separator character (\x1E)
29
+ *
30
+ * @param stream - The readable stream to process
31
+ * @returns An async generator that yields parsed records
32
+ */
33
+ private async *streamProcessor(stream: ReadableStream): AsyncGenerator<WorkflowWatchResult, void, unknown> {
34
+ const reader = stream.getReader();
35
+
36
+ // Track if we've finished reading from the stream
37
+ let doneReading = false;
38
+ // Buffer to accumulate partial chunks
39
+ let buffer = '';
40
+
41
+ try {
42
+ while (!doneReading) {
43
+ // Read the next chunk from the stream
44
+ const { done, value } = await reader.read();
45
+ doneReading = done;
46
+
47
+ // Skip processing if we're done and there's no value
48
+ if (done && !value) continue;
49
+
50
+ try {
51
+ // Decode binary data to text
52
+ const decoded = value ? new TextDecoder().decode(value) : '';
53
+
54
+ // Split the combined buffer and new data by record separator
55
+ const chunks = (buffer + decoded).split(RECORD_SEPARATOR);
56
+
57
+ // The last chunk might be incomplete, so save it for the next iteration
58
+ buffer = chunks.pop() || '';
59
+
60
+ // Process complete chunks
61
+ for (const chunk of chunks) {
62
+ if (chunk) {
63
+ // Only process non-empty chunks
64
+ if (typeof chunk === 'string') {
65
+ try {
66
+ const parsedChunk = JSON.parse(chunk);
67
+ yield parsedChunk;
68
+ } catch {
69
+ // Silently ignore parsing errors to maintain stream processing
70
+ // This allows the stream to continue even if one record is malformed
71
+ }
72
+ }
73
+ }
74
+ }
75
+ } catch {
76
+ // Silently ignore parsing errors to maintain stream processing
77
+ // This allows the stream to continue even if one record is malformed
78
+ }
79
+ }
80
+
81
+ // Process any remaining data in the buffer after stream is done
82
+ if (buffer) {
83
+ try {
84
+ yield JSON.parse(buffer);
85
+ } catch {
86
+ // Ignore parsing error for final chunk
87
+ }
88
+ }
89
+ } finally {
90
+ // Always ensure we clean up the reader
91
+ reader.cancel().catch(() => {
92
+ // Ignore cancel errors
93
+ });
94
+ }
95
+ }
96
+
15
97
  /**
16
98
  * Retrieves details about the workflow
17
99
  * @returns Promise containing workflow details including steps and graphs
@@ -21,21 +103,80 @@ export class Workflow extends BaseResource {
21
103
  }
22
104
 
23
105
  /**
24
- * @deprecated Use `startAsync` instead
25
- * Executes the workflow with the provided parameters
26
- * @param params - Parameters required for workflow execution
27
- * @returns Promise containing the workflow execution results
106
+ * Retrieves all runs for a workflow
107
+ * @param params - Parameters for filtering runs
108
+ * @returns Promise containing workflow runs array
28
109
  */
29
- execute(params: Record<string, any>): Promise<WorkflowRunResult> {
30
- return this.request(`/api/workflows/${this.workflowId}/execute`, {
110
+ runs(params?: GetWorkflowRunsParams): Promise<GetWorkflowRunsResponse> {
111
+ const searchParams = new URLSearchParams();
112
+ if (params?.fromDate) {
113
+ searchParams.set('fromDate', params.fromDate.toISOString());
114
+ }
115
+ if (params?.toDate) {
116
+ searchParams.set('toDate', params.toDate.toISOString());
117
+ }
118
+ if (params?.limit !== null && params?.limit !== undefined && !isNaN(Number(params?.limit))) {
119
+ searchParams.set('limit', String(params.limit));
120
+ }
121
+ if (params?.offset !== null && params?.offset !== undefined && !isNaN(Number(params?.offset))) {
122
+ searchParams.set('offset', String(params.offset));
123
+ }
124
+ if (params?.resourceId) {
125
+ searchParams.set('resourceId', params.resourceId);
126
+ }
127
+
128
+ if (searchParams.size) {
129
+ return this.request(`/api/workflows/${this.workflowId}/runs?${searchParams}`);
130
+ } else {
131
+ return this.request(`/api/workflows/${this.workflowId}/runs`);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Retrieves a specific workflow run by its ID
137
+ * @param runId - The ID of the workflow run to retrieve
138
+ * @returns Promise containing the workflow run details
139
+ */
140
+ runById(runId: string): Promise<GetWorkflowRunByIdResponse> {
141
+ return this.request(`/api/workflows/${this.workflowId}/runs/${runId}`);
142
+ }
143
+
144
+ /**
145
+ * Retrieves the execution result for a specific workflow run by its ID
146
+ * @param runId - The ID of the workflow run to retrieve the execution result for
147
+ * @returns Promise containing the workflow run execution result
148
+ */
149
+ runExecutionResult(runId: string): Promise<GetWorkflowRunExecutionResultResponse> {
150
+ return this.request(`/api/workflows/${this.workflowId}/runs/${runId}/execution-result`);
151
+ }
152
+
153
+ /**
154
+ * Cancels a specific workflow run by its ID
155
+ * @param runId - The ID of the workflow run to cancel
156
+ * @returns Promise containing a success message
157
+ */
158
+ cancelRun(runId: string): Promise<{ message: string }> {
159
+ return this.request(`/api/workflows/${this.workflowId}/runs/${runId}/cancel`, {
160
+ method: 'POST',
161
+ });
162
+ }
163
+
164
+ /**
165
+ * Sends an event to a specific workflow run by its ID
166
+ * @param params - Object containing the runId, event and data
167
+ * @returns Promise containing a success message
168
+ */
169
+ sendRunEvent(params: { runId: string; event: string; data: unknown }): Promise<{ message: string }> {
170
+ return this.request(`/api/workflows/${this.workflowId}/runs/${params.runId}/send-event`, {
31
171
  method: 'POST',
32
- body: params,
172
+ body: { event: params.event, data: params.data },
33
173
  });
34
174
  }
35
175
 
36
176
  /**
37
177
  * Creates a new workflow run
38
- * @returns Promise containing the generated run ID
178
+ * @param params - Optional object containing the optional runId
179
+ * @returns Promise containing the runId of the created run
39
180
  */
40
181
  createRun(params?: { runId?: string }): Promise<{ runId: string }> {
41
182
  const searchParams = new URLSearchParams();
@@ -44,150 +185,162 @@ export class Workflow extends BaseResource {
44
185
  searchParams.set('runId', params.runId);
45
186
  }
46
187
 
47
- return this.request(`/api/workflows/${this.workflowId}/createRun?${searchParams.toString()}`, {
188
+ return this.request(`/api/workflows/${this.workflowId}/create-run?${searchParams.toString()}`, {
48
189
  method: 'POST',
49
190
  });
50
191
  }
51
192
 
52
193
  /**
53
194
  * Starts a workflow run synchronously without waiting for the workflow to complete
54
- * @param params - Object containing the runId and triggerData
195
+ * @param params - Object containing the runId, inputData and runtimeContext
55
196
  * @returns Promise containing success message
56
197
  */
57
- start(params: { runId: string; triggerData: Record<string, any> }): Promise<{ message: string }> {
198
+ start(params: {
199
+ runId: string;
200
+ inputData: Record<string, any>;
201
+ runtimeContext?: RuntimeContext | Record<string, any>;
202
+ }): Promise<{ message: string }> {
203
+ const runtimeContext = parseClientRuntimeContext(params.runtimeContext);
58
204
  return this.request(`/api/workflows/${this.workflowId}/start?runId=${params.runId}`, {
59
205
  method: 'POST',
60
- body: params?.triggerData,
206
+ body: { inputData: params?.inputData, runtimeContext },
61
207
  });
62
208
  }
63
209
 
64
210
  /**
65
211
  * Resumes a suspended workflow step synchronously without waiting for the workflow to complete
66
- * @param stepId - ID of the step to resume
67
- * @param runId - ID of the workflow run
68
- * @param context - Context to resume the workflow with
69
- * @returns Promise containing the workflow resume results
212
+ * @param params - Object containing the runId, step, resumeData and runtimeContext
213
+ * @returns Promise containing success message
70
214
  */
71
215
  resume({
72
- stepId,
216
+ step,
73
217
  runId,
74
- context,
218
+ resumeData,
219
+ ...rest
75
220
  }: {
76
- stepId: string;
221
+ step: string | string[];
77
222
  runId: string;
78
- context: Record<string, any>;
223
+ resumeData?: Record<string, any>;
224
+ runtimeContext?: RuntimeContext | Record<string, any>;
79
225
  }): Promise<{ message: string }> {
226
+ const runtimeContext = parseClientRuntimeContext(rest.runtimeContext);
80
227
  return this.request(`/api/workflows/${this.workflowId}/resume?runId=${runId}`, {
81
228
  method: 'POST',
229
+ stream: true,
82
230
  body: {
83
- stepId,
84
- context,
231
+ step,
232
+ resumeData,
233
+ runtimeContext,
85
234
  },
86
235
  });
87
236
  }
88
237
 
89
238
  /**
90
239
  * Starts a workflow run asynchronously and returns a promise that resolves when the workflow is complete
91
- * @param params - Object containing the optional runId and triggerData
240
+ * @param params - Object containing the optional runId, inputData and runtimeContext
92
241
  * @returns Promise containing the workflow execution results
93
242
  */
94
- startAsync(params: { runId?: string; triggerData: Record<string, any> }): Promise<WorkflowRunResult> {
243
+ startAsync(params: {
244
+ runId?: string;
245
+ inputData: Record<string, any>;
246
+ runtimeContext?: RuntimeContext | Record<string, any>;
247
+ }): Promise<WorkflowRunResult> {
95
248
  const searchParams = new URLSearchParams();
96
249
 
97
250
  if (!!params?.runId) {
98
251
  searchParams.set('runId', params.runId);
99
252
  }
100
253
 
254
+ const runtimeContext = parseClientRuntimeContext(params.runtimeContext);
255
+
101
256
  return this.request(`/api/workflows/${this.workflowId}/start-async?${searchParams.toString()}`, {
102
257
  method: 'POST',
103
- body: params?.triggerData,
258
+ body: { inputData: params.inputData, runtimeContext },
104
259
  });
105
260
  }
106
261
 
107
262
  /**
108
- * Resumes a suspended workflow step asynchronously and returns a promise that resolves when the workflow is complete
109
- * @param params - Object containing the runId, stepId, and context
110
- * @returns Promise containing the workflow resume results
263
+ * Starts a workflow run and returns a stream
264
+ * @param params - Object containing the optional runId, inputData and runtimeContext
265
+ * @returns Promise containing the workflow execution results
111
266
  */
112
- resumeAsync(params: { runId: string; stepId: string; context: Record<string, any> }): Promise<WorkflowRunResult> {
113
- return this.request(`/api/workflows/${this.workflowId}/resume-async?runId=${params.runId}`, {
114
- method: 'POST',
115
- body: {
116
- stepId: params.stepId,
117
- context: params.context,
118
- },
119
- });
120
- }
267
+ async stream(params: { runId?: string; inputData: Record<string, any>; runtimeContext?: RuntimeContext }) {
268
+ const searchParams = new URLSearchParams();
121
269
 
122
- /**
123
- * Creates an async generator that processes a readable stream and yields records
124
- * separated by the Record Separator character (\x1E)
125
- *
126
- * @param stream - The readable stream to process
127
- * @returns An async generator that yields parsed records
128
- */
129
- private async *streamProcessor(stream: ReadableStream): AsyncGenerator<WorkflowRunResult, void, unknown> {
130
- const reader = stream.getReader();
270
+ if (!!params?.runId) {
271
+ searchParams.set('runId', params.runId);
272
+ }
131
273
 
132
- // Track if we've finished reading from the stream
133
- let doneReading = false;
134
- // Buffer to accumulate partial chunks
135
- let buffer = '';
274
+ const runtimeContext = parseClientRuntimeContext(params.runtimeContext);
275
+ const response: Response = await this.request(
276
+ `/api/workflows/${this.workflowId}/stream?${searchParams.toString()}`,
277
+ {
278
+ method: 'POST',
279
+ body: { inputData: params.inputData, runtimeContext },
280
+ stream: true,
281
+ },
282
+ );
136
283
 
137
- try {
138
- while (!doneReading) {
139
- // Read the next chunk from the stream
140
- const { done, value } = await reader.read();
141
- doneReading = done;
284
+ if (!response.ok) {
285
+ throw new Error(`Failed to stream vNext workflow: ${response.statusText}`);
286
+ }
142
287
 
143
- // Skip processing if we're done and there's no value
144
- if (done && !value) continue;
288
+ if (!response.body) {
289
+ throw new Error('Response body is null');
290
+ }
145
291
 
292
+ // Create a transform stream that processes the response body
293
+ const transformStream = new TransformStream<ArrayBuffer, { type: string; payload: any }>({
294
+ start() {},
295
+ async transform(chunk, controller) {
146
296
  try {
147
297
  // Decode binary data to text
148
- const decoded = value ? new TextDecoder().decode(value) : '';
149
-
150
- // Split the combined buffer and new data by record separator
151
- const chunks = (buffer + decoded).split(RECORD_SEPARATOR);
298
+ const decoded = new TextDecoder().decode(chunk);
152
299
 
153
- // The last chunk might be incomplete, so save it for the next iteration
154
- buffer = chunks.pop() || '';
300
+ // Split by record separator
301
+ const chunks = decoded.split(RECORD_SEPARATOR);
155
302
 
156
- // Process complete chunks
303
+ // Process each chunk
157
304
  for (const chunk of chunks) {
158
305
  if (chunk) {
159
- // Only process non-empty chunks
160
- if (typeof chunk === 'string') {
161
- try {
162
- const parsedChunk = JSON.parse(chunk);
163
- yield parsedChunk;
164
- } catch {
165
- // Silently ignore parsing errors to maintain stream processing
166
- // This allows the stream to continue even if one record is malformed
167
- }
306
+ try {
307
+ const parsedChunk = JSON.parse(chunk);
308
+ controller.enqueue(parsedChunk);
309
+ } catch {
310
+ // Silently ignore parsing errors
168
311
  }
169
312
  }
170
313
  }
171
- } catch (error) {
172
- // Silently ignore parsing errors to maintain stream processing
173
- // This allows the stream to continue even if one record is malformed
174
- }
175
- }
176
-
177
- // Process any remaining data in the buffer after stream is done
178
- if (buffer) {
179
- try {
180
- yield JSON.parse(buffer);
181
314
  } catch {
182
- // Ignore parsing error for final chunk
315
+ // Silently ignore processing errors
183
316
  }
184
- }
185
- } finally {
186
- // Always ensure we clean up the reader
187
- reader.cancel().catch(() => {
188
- // Ignore cancel errors
189
- });
190
- }
317
+ },
318
+ });
319
+
320
+ // Pipe the response body through the transform stream
321
+ return response.body.pipeThrough(transformStream);
322
+ }
323
+
324
+ /**
325
+ * Resumes a suspended workflow step asynchronously and returns a promise that resolves when the workflow is complete
326
+ * @param params - Object containing the runId, step, resumeData and runtimeContext
327
+ * @returns Promise containing the workflow resume results
328
+ */
329
+ resumeAsync(params: {
330
+ runId: string;
331
+ step: string | string[];
332
+ resumeData?: Record<string, any>;
333
+ runtimeContext?: RuntimeContext | Record<string, any>;
334
+ }): Promise<WorkflowRunResult> {
335
+ const runtimeContext = parseClientRuntimeContext(params.runtimeContext);
336
+ return this.request(`/api/workflows/${this.workflowId}/resume-async?runId=${params.runId}`, {
337
+ method: 'POST',
338
+ body: {
339
+ step: params.step,
340
+ resumeData: params.resumeData,
341
+ runtimeContext,
342
+ },
343
+ });
191
344
  }
192
345
 
193
346
  /**
@@ -195,7 +348,7 @@ export class Workflow extends BaseResource {
195
348
  * @param runId - Optional run ID to filter the watch stream
196
349
  * @returns AsyncGenerator that yields parsed records from the workflow watch stream
197
350
  */
198
- async watch({ runId }: { runId?: string }, onRecord: (record: WorkflowRunResult) => void) {
351
+ async watch({ runId }: { runId?: string }, onRecord: (record: WorkflowWatchResult) => void) {
199
352
  const response: Response = await this.request(`/api/workflows/${this.workflowId}/watch?runId=${runId}`, {
200
353
  stream: true,
201
354
  });
@@ -209,7 +362,35 @@ export class Workflow extends BaseResource {
209
362
  }
210
363
 
211
364
  for await (const record of this.streamProcessor(response.body)) {
212
- onRecord(record);
365
+ if (typeof record === 'string') {
366
+ onRecord(JSON.parse(record));
367
+ } else {
368
+ onRecord(record);
369
+ }
213
370
  }
214
371
  }
372
+
373
+ /**
374
+ * Creates a new ReadableStream from an iterable or async iterable of objects,
375
+ * serializing each as JSON and separating them with the record separator (\x1E).
376
+ *
377
+ * @param records - An iterable or async iterable of objects to stream
378
+ * @returns A ReadableStream emitting the records as JSON strings separated by the record separator
379
+ */
380
+ static createRecordStream(records: Iterable<any> | AsyncIterable<any>): ReadableStream {
381
+ const encoder = new TextEncoder();
382
+ return new ReadableStream({
383
+ async start(controller) {
384
+ try {
385
+ for await (const record of records as AsyncIterable<any>) {
386
+ const json = JSON.stringify(record) + RECORD_SEPARATOR;
387
+ controller.enqueue(encoder.encode(json));
388
+ }
389
+ controller.close();
390
+ } catch (err) {
391
+ controller.error(err);
392
+ }
393
+ },
394
+ });
395
+ }
215
396
  }