@mastra/client-js 0.0.0-course-20250527170450 → 0.0.0-custom-instrumentation-20250708222033

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.
@@ -0,0 +1,63 @@
1
+ import type { StorageThreadType } from '@mastra/core';
2
+
3
+ import type {
4
+ GetMemoryThreadMessagesResponse,
5
+ ClientOptions,
6
+ UpdateMemoryThreadParams,
7
+ GetMemoryThreadMessagesParams,
8
+ } from '../types';
9
+
10
+ import { BaseResource } from './base';
11
+
12
+ export class NetworkMemoryThread extends BaseResource {
13
+ constructor(
14
+ options: ClientOptions,
15
+ private threadId: string,
16
+ private networkId: string,
17
+ ) {
18
+ super(options);
19
+ }
20
+
21
+ /**
22
+ * Retrieves the memory thread details
23
+ * @returns Promise containing thread details including title and metadata
24
+ */
25
+ get(): Promise<StorageThreadType> {
26
+ return this.request(`/api/memory/network/threads/${this.threadId}?networkId=${this.networkId}`);
27
+ }
28
+
29
+ /**
30
+ * Updates the memory thread properties
31
+ * @param params - Update parameters including title and metadata
32
+ * @returns Promise containing updated thread details
33
+ */
34
+ update(params: UpdateMemoryThreadParams): Promise<StorageThreadType> {
35
+ return this.request(`/api/memory/network/threads/${this.threadId}?networkId=${this.networkId}`, {
36
+ method: 'PATCH',
37
+ body: params,
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Deletes the memory thread
43
+ * @returns Promise containing deletion result
44
+ */
45
+ delete(): Promise<{ result: string }> {
46
+ return this.request(`/api/memory/network/threads/${this.threadId}?networkId=${this.networkId}`, {
47
+ method: 'DELETE',
48
+ });
49
+ }
50
+
51
+ /**
52
+ * Retrieves messages associated with the thread
53
+ * @param params - Optional parameters including limit for number of messages to retrieve
54
+ * @returns Promise containing thread messages and UI messages
55
+ */
56
+ getMessages(params?: GetMemoryThreadMessagesParams): Promise<GetMemoryThreadMessagesResponse> {
57
+ const query = new URLSearchParams({
58
+ networkId: this.networkId,
59
+ ...(params?.limit ? { limit: params.limit.toString() } : {}),
60
+ });
61
+ return this.request(`/api/memory/network/threads/${this.threadId}/messages?${query.toString()}`);
62
+ }
63
+ }
@@ -1,10 +1,9 @@
1
1
  import { processDataStream } from '@ai-sdk/ui-utils';
2
2
  import type { GenerateReturn } from '@mastra/core';
3
3
  import type { JSONSchema7 } from 'json-schema';
4
- import { ZodSchema } from 'zod';
5
- import { zodToJsonSchema } from '../utils/zod-to-json-schema';
6
-
4
+ import type { ZodSchema } from 'zod';
7
5
  import type { GenerateParams, ClientOptions, StreamParams, GetNetworkResponse } from '../types';
6
+ import { zodToJsonSchema } from '../utils/zod-to-json-schema';
8
7
 
9
8
  import { BaseResource } from './base';
10
9
 
@@ -0,0 +1,177 @@
1
+ import type { WatchEvent } from '@mastra/core/workflows';
2
+
3
+ import type {
4
+ ClientOptions,
5
+ GetVNextNetworkResponse,
6
+ GenerateVNextNetworkResponse,
7
+ LoopVNextNetworkResponse,
8
+ GenerateOrStreamVNextNetworkParams,
9
+ LoopStreamVNextNetworkParams,
10
+ } from '../types';
11
+
12
+ import { BaseResource } from './base';
13
+
14
+ const RECORD_SEPARATOR = '\x1E';
15
+
16
+ export class VNextNetwork extends BaseResource {
17
+ constructor(
18
+ options: ClientOptions,
19
+ private networkId: string,
20
+ ) {
21
+ super(options);
22
+ }
23
+
24
+ /**
25
+ * Retrieves details about the network
26
+ * @returns Promise containing vNext network details
27
+ */
28
+ details(): Promise<GetVNextNetworkResponse> {
29
+ return this.request(`/api/networks/v-next/${this.networkId}`);
30
+ }
31
+
32
+ /**
33
+ * Generates a response from the v-next network
34
+ * @param params - Generation parameters including message
35
+ * @returns Promise containing the generated response
36
+ */
37
+ generate(params: GenerateOrStreamVNextNetworkParams): Promise<GenerateVNextNetworkResponse> {
38
+ return this.request(`/api/networks/v-next/${this.networkId}/generate`, {
39
+ method: 'POST',
40
+ body: params,
41
+ });
42
+ }
43
+
44
+ /**
45
+ * Generates a response from the v-next network using multiple primitives
46
+ * @param params - Generation parameters including message
47
+ * @returns Promise containing the generated response
48
+ */
49
+ loop(params: { message: string }): Promise<LoopVNextNetworkResponse> {
50
+ return this.request(`/api/networks/v-next/${this.networkId}/loop`, {
51
+ method: 'POST',
52
+ body: params,
53
+ });
54
+ }
55
+
56
+ private async *streamProcessor(stream: ReadableStream): AsyncGenerator<WatchEvent, void, unknown> {
57
+ const reader = stream.getReader();
58
+
59
+ // Track if we've finished reading from the stream
60
+ let doneReading = false;
61
+ // Buffer to accumulate partial chunks
62
+ let buffer = '';
63
+
64
+ try {
65
+ while (!doneReading) {
66
+ // Read the next chunk from the stream
67
+ const { done, value } = await reader.read();
68
+ doneReading = done;
69
+
70
+ // Skip processing if we're done and there's no value
71
+ if (done && !value) continue;
72
+
73
+ try {
74
+ // Decode binary data to text
75
+ const decoded = value ? new TextDecoder().decode(value) : '';
76
+
77
+ // Split the combined buffer and new data by record separator
78
+ const chunks = (buffer + decoded).split(RECORD_SEPARATOR);
79
+
80
+ // The last chunk might be incomplete, so save it for the next iteration
81
+ buffer = chunks.pop() || '';
82
+
83
+ // Process complete chunks
84
+ for (const chunk of chunks) {
85
+ if (chunk) {
86
+ // Only process non-empty chunks
87
+ if (typeof chunk === 'string') {
88
+ try {
89
+ const parsedChunk = JSON.parse(chunk);
90
+ yield parsedChunk;
91
+ } catch {
92
+ // Silently ignore parsing errors to maintain stream processing
93
+ // This allows the stream to continue even if one record is malformed
94
+ }
95
+ }
96
+ }
97
+ }
98
+ } catch {
99
+ // Silently ignore parsing errors to maintain stream processing
100
+ // This allows the stream to continue even if one record is malformed
101
+ }
102
+ }
103
+
104
+ // Process any remaining data in the buffer after stream is done
105
+ if (buffer) {
106
+ try {
107
+ yield JSON.parse(buffer);
108
+ } catch {
109
+ // Ignore parsing error for final chunk
110
+ }
111
+ }
112
+ } finally {
113
+ // Always ensure we clean up the reader
114
+ reader.cancel().catch(() => {
115
+ // Ignore cancel errors
116
+ });
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Streams a response from the v-next network
122
+ * @param params - Stream parameters including message
123
+ * @returns Promise containing the results
124
+ */
125
+ async stream(params: GenerateOrStreamVNextNetworkParams, onRecord: (record: WatchEvent) => void) {
126
+ const response: Response = await this.request(`/api/networks/v-next/${this.networkId}/stream`, {
127
+ method: 'POST',
128
+ body: params,
129
+ stream: true,
130
+ });
131
+
132
+ if (!response.ok) {
133
+ throw new Error(`Failed to stream vNext network: ${response.statusText}`);
134
+ }
135
+
136
+ if (!response.body) {
137
+ throw new Error('Response body is null');
138
+ }
139
+
140
+ for await (const record of this.streamProcessor(response.body)) {
141
+ if (typeof record === 'string') {
142
+ onRecord(JSON.parse(record));
143
+ } else {
144
+ onRecord(record);
145
+ }
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Streams a response from the v-next network loop
151
+ * @param params - Stream parameters including message
152
+ * @returns Promise containing the results
153
+ */
154
+ async loopStream(params: LoopStreamVNextNetworkParams, onRecord: (record: WatchEvent) => void) {
155
+ const response: Response = await this.request(`/api/networks/v-next/${this.networkId}/loop-stream`, {
156
+ method: 'POST',
157
+ body: params,
158
+ stream: true,
159
+ });
160
+
161
+ if (!response.ok) {
162
+ throw new Error(`Failed to stream vNext network loop: ${response.statusText}`);
163
+ }
164
+
165
+ if (!response.body) {
166
+ throw new Error('Response body is null');
167
+ }
168
+
169
+ for await (const record of this.streamProcessor(response.body)) {
170
+ if (typeof record === 'string') {
171
+ onRecord(JSON.parse(record));
172
+ } else {
173
+ onRecord(record);
174
+ }
175
+ }
176
+ }
177
+ }
@@ -6,6 +6,8 @@ import type {
6
6
  GetWorkflowRunsParams,
7
7
  WorkflowRunResult,
8
8
  WorkflowWatchResult,
9
+ GetWorkflowRunByIdResponse,
10
+ GetWorkflowRunExecutionResultResponse,
9
11
  } from '../types';
10
12
 
11
13
  import { parseClientRuntimeContext } from '../utils';
@@ -113,10 +115,10 @@ export class Workflow extends BaseResource {
113
115
  if (params?.toDate) {
114
116
  searchParams.set('toDate', params.toDate.toISOString());
115
117
  }
116
- if (params?.limit) {
118
+ if (params?.limit !== null && params?.limit !== undefined && !isNaN(Number(params?.limit))) {
117
119
  searchParams.set('limit', String(params.limit));
118
120
  }
119
- if (params?.offset) {
121
+ if (params?.offset !== null && params?.offset !== undefined && !isNaN(Number(params?.offset))) {
120
122
  searchParams.set('offset', String(params.offset));
121
123
  }
122
124
  if (params?.resourceId) {
@@ -130,6 +132,47 @@ export class Workflow extends BaseResource {
130
132
  }
131
133
  }
132
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`, {
171
+ method: 'POST',
172
+ body: { event: params.event, data: params.data },
173
+ });
174
+ }
175
+
133
176
  /**
134
177
  * Creates a new workflow run
135
178
  * @param params - Optional object containing the optional runId
@@ -217,9 +260,9 @@ export class Workflow extends BaseResource {
217
260
  }
218
261
 
219
262
  /**
220
- * Starts a vNext workflow run and returns a stream
263
+ * Starts a workflow run and returns a stream
221
264
  * @param params - Object containing the optional runId, inputData and runtimeContext
222
- * @returns Promise containing the vNext workflow execution results
265
+ * @returns Promise containing the workflow execution results
223
266
  */
224
267
  async stream(params: { runId?: string; inputData: Record<string, any>; runtimeContext?: RuntimeContext }) {
225
268
  const searchParams = new URLSearchParams();
@@ -228,7 +271,7 @@ export class Workflow extends BaseResource {
228
271
  searchParams.set('runId', params.runId);
229
272
  }
230
273
 
231
- const runtimeContext = params.runtimeContext ? Object.fromEntries(params.runtimeContext.entries()) : undefined;
274
+ const runtimeContext = parseClientRuntimeContext(params.runtimeContext);
232
275
  const response: Response = await this.request(
233
276
  `/api/workflows/${this.workflowId}/stream?${searchParams.toString()}`,
234
277
  {
@@ -247,7 +290,7 @@ export class Workflow extends BaseResource {
247
290
  }
248
291
 
249
292
  // Create a transform stream that processes the response body
250
- const transformStream = new TransformStream<ArrayBuffer, WorkflowWatchResult>({
293
+ const transformStream = new TransformStream<ArrayBuffer, { type: string; payload: any }>({
251
294
  start() {},
252
295
  async transform(chunk, controller) {
253
296
  try {
@@ -319,7 +362,11 @@ export class Workflow extends BaseResource {
319
362
  }
320
363
 
321
364
  for await (const record of this.streamProcessor(response.body)) {
322
- onRecord(record);
365
+ if (typeof record === 'string') {
366
+ onRecord(JSON.parse(record));
367
+ } else {
368
+ onRecord(record);
369
+ }
323
370
  }
324
371
  }
325
372
 
package/src/types.ts CHANGED
@@ -1,16 +1,17 @@
1
1
  import type {
2
- MessageType,
2
+ MastraMessageV1,
3
3
  AiMessageType,
4
4
  CoreMessage,
5
5
  QueryResult,
6
6
  StorageThreadType,
7
7
  WorkflowRuns,
8
+ WorkflowRun,
8
9
  LegacyWorkflowRuns,
9
10
  } from '@mastra/core';
10
- import type { BaseLogMessage } from '@mastra/core/logger';
11
+ import type { AgentGenerateOptions, AgentStreamOptions, ToolsInput } from '@mastra/core/agent';
12
+ import type { BaseLogMessage, LogLevel } from '@mastra/core/logger';
11
13
 
12
- import type { AgentGenerateOptions, AgentStreamOptions } from '@mastra/core/agent';
13
- import type { ServerInfo } from '@mastra/core/mcp';
14
+ import type { MCPToolType, ServerInfo } from '@mastra/core/mcp';
14
15
  import type { RuntimeContext } from '@mastra/core/runtime-context';
15
16
  import type { Workflow, WatchEvent, WorkflowResult } from '@mastra/core/workflows';
16
17
  import type {
@@ -33,6 +34,7 @@ export interface ClientOptions {
33
34
  /** Custom headers to include with requests */
34
35
  headers?: Record<string, string>;
35
36
  /** Abort signal for request */
37
+ abortSignal?: AbortSignal;
36
38
  }
37
39
 
38
40
  export interface RequestOptions {
@@ -40,7 +42,6 @@ export interface RequestOptions {
40
42
  headers?: Record<string, string>;
41
43
  body?: any;
42
44
  stream?: boolean;
43
- signal?: AbortSignal;
44
45
  }
45
46
 
46
47
  type WithoutMethods<T> = {
@@ -69,14 +70,20 @@ export type GenerateParams<T extends JSONSchema7 | ZodSchema | undefined = undef
69
70
  output?: T;
70
71
  experimental_output?: T;
71
72
  runtimeContext?: RuntimeContext | Record<string, any>;
72
- } & WithoutMethods<Omit<AgentGenerateOptions<T>, 'output' | 'experimental_output' | 'runtimeContext'>>;
73
+ clientTools?: ToolsInput;
74
+ } & WithoutMethods<
75
+ Omit<AgentGenerateOptions<T>, 'output' | 'experimental_output' | 'runtimeContext' | 'clientTools' | 'abortSignal'>
76
+ >;
73
77
 
74
78
  export type StreamParams<T extends JSONSchema7 | ZodSchema | undefined = undefined> = {
75
79
  messages: string | string[] | CoreMessage[] | AiMessageType[];
76
80
  output?: T;
77
81
  experimental_output?: T;
78
82
  runtimeContext?: RuntimeContext | Record<string, any>;
79
- } & WithoutMethods<Omit<AgentStreamOptions<T>, 'output' | 'experimental_output' | 'runtimeContext'>>;
83
+ clientTools?: ToolsInput;
84
+ } & WithoutMethods<
85
+ Omit<AgentStreamOptions<T>, 'output' | 'experimental_output' | 'runtimeContext' | 'clientTools' | 'abortSignal'>
86
+ >;
80
87
 
81
88
  export interface GetEvalsByAgentIdResponse extends GetAgentResponse {
82
89
  evals: any[];
@@ -113,6 +120,10 @@ export type GetLegacyWorkflowRunsResponse = LegacyWorkflowRuns;
113
120
 
114
121
  export type GetWorkflowRunsResponse = WorkflowRuns;
115
122
 
123
+ export type GetWorkflowRunByIdResponse = WorkflowRun;
124
+
125
+ export type GetWorkflowRunExecutionResultResponse = WatchEvent['payload']['workflowState'];
126
+
116
127
  export type LegacyWorkflowRunResult = {
117
128
  activePaths: Record<string, { status: string; suspendPayload?: any; stepPath: string[] }>;
118
129
  results: CoreLegacyWorkflowRunResult<any, any, any>['results'];
@@ -133,6 +144,17 @@ export interface GetWorkflowResponse {
133
144
  suspendSchema: string;
134
145
  };
135
146
  };
147
+ allSteps: {
148
+ [key: string]: {
149
+ id: string;
150
+ description: string;
151
+ inputSchema: string;
152
+ outputSchema: string;
153
+ resumeSchema: string;
154
+ suspendSchema: string;
155
+ isWorkflow: boolean;
156
+ };
157
+ };
136
158
  stepGraph: Workflow['serializedStepGraph'];
137
159
  inputSchema: string;
138
160
  outputSchema: string;
@@ -172,11 +194,16 @@ export interface GetVectorIndexResponse {
172
194
  }
173
195
 
174
196
  export interface SaveMessageToMemoryParams {
175
- messages: MessageType[];
197
+ messages: MastraMessageV1[];
176
198
  agentId: string;
177
199
  }
178
200
 
179
- export type SaveMessageToMemoryResponse = MessageType[];
201
+ export interface SaveNetworkMessageToMemoryParams {
202
+ messages: MastraMessageV1[];
203
+ networkId: string;
204
+ }
205
+
206
+ export type SaveMessageToMemoryResponse = MastraMessageV1[];
180
207
 
181
208
  export interface CreateMemoryThreadParams {
182
209
  title?: string;
@@ -186,6 +213,14 @@ export interface CreateMemoryThreadParams {
186
213
  agentId: string;
187
214
  }
188
215
 
216
+ export interface CreateNetworkMemoryThreadParams {
217
+ title?: string;
218
+ metadata?: Record<string, any>;
219
+ resourceId: string;
220
+ threadId?: string;
221
+ networkId: string;
222
+ }
223
+
189
224
  export type CreateMemoryThreadResponse = StorageThreadType;
190
225
 
191
226
  export interface GetMemoryThreadParams {
@@ -193,6 +228,11 @@ export interface GetMemoryThreadParams {
193
228
  agentId: string;
194
229
  }
195
230
 
231
+ export interface GetNetworkMemoryThreadParams {
232
+ resourceId: string;
233
+ networkId: string;
234
+ }
235
+
196
236
  export type GetMemoryThreadResponse = StorageThreadType[];
197
237
 
198
238
  export interface UpdateMemoryThreadParams {
@@ -215,14 +255,32 @@ export interface GetMemoryThreadMessagesResponse {
215
255
 
216
256
  export interface GetLogsParams {
217
257
  transportId: string;
258
+ fromDate?: Date;
259
+ toDate?: Date;
260
+ logLevel?: LogLevel;
261
+ filters?: Record<string, string>;
262
+ page?: number;
263
+ perPage?: number;
218
264
  }
219
265
 
220
266
  export interface GetLogParams {
221
267
  runId: string;
222
268
  transportId: string;
269
+ fromDate?: Date;
270
+ toDate?: Date;
271
+ logLevel?: LogLevel;
272
+ filters?: Record<string, string>;
273
+ page?: number;
274
+ perPage?: number;
223
275
  }
224
276
 
225
- export type GetLogsResponse = BaseLogMessage[];
277
+ export type GetLogsResponse = {
278
+ logs: BaseLogMessage[];
279
+ total: number;
280
+ page: number;
281
+ perPage: number;
282
+ hasMore: boolean;
283
+ };
226
284
 
227
285
  export type RequestFunction = (path: string, options?: RequestOptions) => Promise<any>;
228
286
 
@@ -281,6 +339,7 @@ export interface GetTelemetryParams {
281
339
  }
282
340
 
283
341
  export interface GetNetworkResponse {
342
+ id: string;
284
343
  name: string;
285
344
  instructions: string;
286
345
  agents: Array<{
@@ -295,6 +354,59 @@ export interface GetNetworkResponse {
295
354
  state?: Record<string, any>;
296
355
  }
297
356
 
357
+ export interface GetVNextNetworkResponse {
358
+ id: string;
359
+ name: string;
360
+ instructions: string;
361
+ agents: Array<{
362
+ name: string;
363
+ provider: string;
364
+ modelId: string;
365
+ }>;
366
+ routingModel: {
367
+ provider: string;
368
+ modelId: string;
369
+ };
370
+ workflows: Array<{
371
+ name: string;
372
+ description: string;
373
+ inputSchema: string | undefined;
374
+ outputSchema: string | undefined;
375
+ }>;
376
+ tools: Array<{
377
+ id: string;
378
+ description: string;
379
+ }>;
380
+ }
381
+
382
+ export interface GenerateVNextNetworkResponse {
383
+ task: string;
384
+ result: string;
385
+ resourceId: string;
386
+ resourceType: 'none' | 'tool' | 'agent' | 'workflow';
387
+ }
388
+
389
+ export interface GenerateOrStreamVNextNetworkParams {
390
+ message: string;
391
+ threadId?: string;
392
+ resourceId?: string;
393
+ }
394
+
395
+ export interface LoopStreamVNextNetworkParams {
396
+ message: string;
397
+ threadId?: string;
398
+ resourceId?: string;
399
+ maxIterations?: number;
400
+ }
401
+
402
+ export interface LoopVNextNetworkResponse {
403
+ status: 'success';
404
+ result: {
405
+ text: string;
406
+ };
407
+ steps: WorkflowResult<any, any>['steps'];
408
+ }
409
+
298
410
  export interface McpServerListResponse {
299
411
  servers: ServerInfo[];
300
412
  next: string | null;
@@ -306,6 +418,7 @@ export interface McpToolInfo {
306
418
  name: string;
307
419
  description?: string;
308
420
  inputSchema: string;
421
+ toolType?: MCPToolType;
309
422
  }
310
423
 
311
424
  export interface McpServerToolListResponse {
@@ -0,0 +1,32 @@
1
+ import { isVercelTool } from '@mastra/core/tools';
2
+ import { zodToJsonSchema } from './zod-to-json-schema';
3
+ import type { ToolsInput } from '@mastra/core/agent';
4
+
5
+ export function processClientTools(clientTools: ToolsInput | undefined): ToolsInput | undefined {
6
+ if (!clientTools) {
7
+ return undefined;
8
+ }
9
+
10
+ return Object.fromEntries(
11
+ Object.entries(clientTools).map(([key, value]) => {
12
+ if (isVercelTool(value)) {
13
+ return [
14
+ key,
15
+ {
16
+ ...value,
17
+ parameters: value.parameters ? zodToJsonSchema(value.parameters) : undefined,
18
+ },
19
+ ];
20
+ } else {
21
+ return [
22
+ key,
23
+ {
24
+ ...value,
25
+ inputSchema: value.inputSchema ? zodToJsonSchema(value.inputSchema) : undefined,
26
+ outputSchema: value.outputSchema ? zodToJsonSchema(value.outputSchema) : undefined,
27
+ },
28
+ ];
29
+ }
30
+ }),
31
+ );
32
+ }