@mastra/cloudflare 0.0.0-vnext-inngest-20250508131921 → 0.0.0-vnext-20251119160359

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.js CHANGED
@@ -1,327 +1,447 @@
1
- import { MastraStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_EVALS, TABLE_TRACES } from '@mastra/core/storage';
1
+ import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
2
+ import { MastraStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_SCORERS, StoreOperations, TABLE_TRACES, WorkflowsStorage, ensureDate, normalizePerPage, MemoryStorage, calculatePagination, serializeDate, TABLE_RESOURCES, ScoresStorage, safelyParseJSON } from '@mastra/core/storage';
2
3
  import Cloudflare from 'cloudflare';
4
+ import { MessageList } from '@mastra/core/agent';
5
+ import { saveScorePayloadSchema } from '@mastra/core/evals';
3
6
 
4
7
  // src/storage/index.ts
5
-
6
- // src/storage/types.ts
7
- function isWorkersConfig(config) {
8
- return "bindings" in config;
9
- }
10
-
11
- // src/storage/index.ts
12
- var CloudflareStore = class extends MastraStorage {
13
- client;
14
- accountId;
15
- namespacePrefix;
16
- bindings;
17
- validateWorkersConfig(config) {
18
- if (!isWorkersConfig(config)) {
19
- throw new Error("Invalid Workers API configuration");
20
- }
21
- if (!config.bindings) {
22
- throw new Error("KV bindings are required when using Workers Binding API");
23
- }
24
- const requiredTables = [TABLE_THREADS, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_EVALS, TABLE_TRACES];
25
- for (const table of requiredTables) {
26
- if (!(table in config.bindings)) {
27
- throw new Error(`Missing KV binding for table: ${table}`);
28
- }
29
- }
8
+ var MemoryStorageCloudflare = class extends MemoryStorage {
9
+ operations;
10
+ constructor({ operations }) {
11
+ super();
12
+ this.operations = operations;
30
13
  }
31
- validateRestConfig(config) {
32
- if (isWorkersConfig(config)) {
33
- throw new Error("Invalid REST API configuration");
34
- }
35
- if (!config.accountId?.trim()) {
36
- throw new Error("accountId is required for REST API");
37
- }
38
- if (!config.apiToken?.trim()) {
39
- throw new Error("apiToken is required for REST API");
40
- }
14
+ ensureMetadata(metadata) {
15
+ if (!metadata) return void 0;
16
+ return typeof metadata === "string" ? JSON.parse(metadata) : metadata;
41
17
  }
42
- constructor(config) {
43
- super({ name: "Cloudflare" });
18
+ /**
19
+ * Summarizes message content without exposing raw data (for logging).
20
+ * Returns type, length, and keys only to prevent PII leakage.
21
+ */
22
+ summarizeMessageContent(content) {
23
+ if (!content) return { type: "undefined" };
24
+ if (typeof content === "string") return { type: "string", length: content.length };
25
+ if (Array.isArray(content)) return { type: "array", length: content.length };
26
+ if (typeof content === "object") return { type: "object", keys: Object.keys(content) };
27
+ return { type: typeof content };
28
+ }
29
+ async getThreadById({ threadId }) {
30
+ const thread = await this.operations.load({ tableName: TABLE_THREADS, keys: { id: threadId } });
31
+ if (!thread) return null;
44
32
  try {
45
- if (isWorkersConfig(config)) {
46
- this.validateWorkersConfig(config);
47
- this.bindings = config.bindings;
48
- this.namespacePrefix = config.keyPrefix?.trim() || "";
49
- this.logger.info("Using Cloudflare KV Workers Binding API");
50
- } else {
51
- this.validateRestConfig(config);
52
- this.accountId = config.accountId.trim();
53
- this.namespacePrefix = config.namespacePrefix?.trim() || "";
54
- this.client = new Cloudflare({
55
- apiToken: config.apiToken.trim()
56
- });
57
- this.logger.info("Using Cloudflare KV REST API");
58
- }
33
+ return {
34
+ ...thread,
35
+ createdAt: ensureDate(thread.createdAt),
36
+ updatedAt: ensureDate(thread.updatedAt),
37
+ metadata: this.ensureMetadata(thread.metadata)
38
+ };
59
39
  } catch (error) {
60
- const message = error instanceof Error ? error.message : String(error);
61
- this.logger.error("Failed to initialize CloudflareStore:", { message });
62
- throw error;
40
+ const mastraError = new MastraError(
41
+ {
42
+ id: "CLOUDFLARE_STORAGE_GET_THREAD_BY_ID_FAILED",
43
+ domain: ErrorDomain.STORAGE,
44
+ category: ErrorCategory.THIRD_PARTY,
45
+ details: {
46
+ threadId
47
+ }
48
+ },
49
+ error
50
+ );
51
+ this.logger?.trackException(mastraError);
52
+ this.logger?.error(mastraError.toString());
53
+ return null;
63
54
  }
64
55
  }
65
- getBinding(tableName) {
66
- if (!this.bindings) {
67
- throw new Error(`Cannot use Workers API binding for ${tableName}: Store initialized with REST API configuration`);
68
- }
69
- const binding = this.bindings[tableName];
70
- if (!binding) throw new Error(`No binding found for namespace ${tableName}`);
71
- return binding;
72
- }
73
- async listNamespaces() {
74
- if (this.bindings) {
56
+ async listThreadsByResourceId(args) {
57
+ try {
58
+ const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
59
+ const perPage = normalizePerPage(perPageInput, 100);
60
+ if (page < 0) {
61
+ throw new MastraError(
62
+ {
63
+ id: "STORAGE_CLOUDFLARE_LIST_THREADS_BY_RESOURCE_ID_INVALID_PAGE",
64
+ domain: ErrorDomain.STORAGE,
65
+ category: ErrorCategory.USER,
66
+ details: { page }
67
+ },
68
+ new Error("page must be >= 0")
69
+ );
70
+ }
71
+ const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
72
+ const { field, direction } = this.parseOrderBy(orderBy);
73
+ const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
74
+ const keyObjs = await this.operations.listKV(TABLE_THREADS, { prefix: `${prefix}${TABLE_THREADS}` });
75
+ const threads = [];
76
+ for (const { name: key } of keyObjs) {
77
+ const data = await this.operations.getKV(TABLE_THREADS, key);
78
+ if (!data) continue;
79
+ if (data.resourceId !== resourceId) continue;
80
+ threads.push(data);
81
+ }
82
+ threads.sort((a, b) => {
83
+ const aTime = new Date(a[field] || 0).getTime();
84
+ const bTime = new Date(b[field] || 0).getTime();
85
+ return direction === "ASC" ? aTime - bTime : bTime - aTime;
86
+ });
87
+ const end = perPageInput === false ? threads.length : offset + perPage;
88
+ const paginatedThreads = threads.slice(offset, end);
75
89
  return {
76
- result: Object.keys(this.bindings).map((name) => ({
77
- id: name,
78
- title: name,
79
- supports_url_encoding: true
80
- }))
90
+ page,
91
+ perPage: perPageForResponse,
92
+ total: threads.length,
93
+ hasMore: perPageInput === false ? false : offset + perPage < threads.length,
94
+ threads: paginatedThreads
81
95
  };
96
+ } catch (error) {
97
+ throw new MastraError(
98
+ {
99
+ id: "CLOUDFLARE_STORAGE_LIST_THREADS_BY_RESOURCE_ID_FAILED",
100
+ domain: ErrorDomain.STORAGE,
101
+ category: ErrorCategory.THIRD_PARTY,
102
+ text: "Failed to get threads by resource ID with pagination"
103
+ },
104
+ error
105
+ );
82
106
  }
83
- return await this.client.kv.namespaces.list({ account_id: this.accountId });
84
107
  }
85
- async getNamespaceValue(tableName, key) {
108
+ async saveThread({ thread }) {
86
109
  try {
87
- if (this.bindings) {
88
- const binding = this.getBinding(tableName);
89
- const result = await binding.getWithMetadata(key, "text");
90
- if (!result) return null;
91
- return JSON.stringify(result);
92
- } else {
93
- const namespaceId = await this.getNamespaceId(tableName);
94
- const response = await this.client.kv.namespaces.values.get(namespaceId, key, {
95
- account_id: this.accountId
96
- });
97
- return await response.text();
98
- }
110
+ await this.operations.insert({ tableName: TABLE_THREADS, record: thread });
111
+ return thread;
99
112
  } catch (error) {
100
- if (error.message && error.message.includes("key not found")) {
101
- return null;
102
- }
103
- const message = error instanceof Error ? error.message : String(error);
104
- this.logger.error(`Failed to get value for ${tableName} ${key}:`, { message });
105
- throw error;
113
+ throw new MastraError(
114
+ {
115
+ id: "CLOUDFLARE_STORAGE_SAVE_THREAD_FAILED",
116
+ domain: ErrorDomain.STORAGE,
117
+ category: ErrorCategory.THIRD_PARTY,
118
+ details: {
119
+ threadId: thread.id
120
+ }
121
+ },
122
+ error
123
+ );
106
124
  }
107
125
  }
108
- async putNamespaceValue({
109
- tableName,
110
- key,
111
- value,
126
+ async updateThread({
127
+ id,
128
+ title,
112
129
  metadata
113
130
  }) {
114
131
  try {
115
- const serializedValue = this.safeSerialize(value);
116
- const serializedMetadata = metadata ? this.safeSerialize(metadata) : "";
117
- if (this.bindings) {
118
- const binding = this.getBinding(tableName);
119
- await binding.put(key, serializedValue, { metadata: serializedMetadata });
120
- } else {
121
- const namespaceId = await this.getNamespaceId(tableName);
122
- await this.client.kv.namespaces.values.update(namespaceId, key, {
123
- account_id: this.accountId,
124
- value: serializedValue,
125
- metadata: serializedMetadata
126
- });
132
+ const thread = await this.getThreadById({ threadId: id });
133
+ if (!thread) {
134
+ throw new Error(`Thread ${id} not found`);
127
135
  }
136
+ const updatedThread = {
137
+ ...thread,
138
+ title,
139
+ metadata: this.ensureMetadata({
140
+ ...thread.metadata ?? {},
141
+ ...metadata
142
+ }),
143
+ updatedAt: /* @__PURE__ */ new Date()
144
+ };
145
+ await this.operations.insert({ tableName: TABLE_THREADS, record: updatedThread });
146
+ return updatedThread;
128
147
  } catch (error) {
129
- const message = error instanceof Error ? error.message : String(error);
130
- this.logger.error(`Failed to put value for ${tableName} ${key}:`, { message });
131
- throw error;
132
- }
133
- }
134
- async deleteNamespaceValue(tableName, key) {
135
- if (this.bindings) {
136
- const binding = this.getBinding(tableName);
137
- await binding.delete(key);
138
- } else {
139
- const namespaceId = await this.getNamespaceId(tableName);
140
- await this.client.kv.namespaces.values.delete(namespaceId, key, {
141
- account_id: this.accountId
142
- });
148
+ throw new MastraError(
149
+ {
150
+ id: "CLOUDFLARE_STORAGE_UPDATE_THREAD_FAILED",
151
+ domain: ErrorDomain.STORAGE,
152
+ category: ErrorCategory.THIRD_PARTY,
153
+ details: {
154
+ threadId: id,
155
+ title
156
+ }
157
+ },
158
+ error
159
+ );
143
160
  }
144
161
  }
145
- async listNamespaceKeys(tableName, options) {
162
+ getMessageKey(threadId, messageId) {
146
163
  try {
147
- if (this.bindings) {
148
- const binding = this.getBinding(tableName);
149
- const response = await binding.list({
150
- limit: options?.limit || 1e3,
151
- prefix: options?.prefix
152
- });
153
- return response.keys;
154
- } else {
155
- const namespaceId = await this.getNamespaceId(tableName);
156
- const response = await this.client.kv.namespaces.keys.list(namespaceId, {
157
- account_id: this.accountId,
158
- limit: options?.limit || 1e3,
159
- prefix: options?.prefix
160
- });
161
- return response.result;
162
- }
164
+ return this.operations.getKey(TABLE_MESSAGES, { threadId, id: messageId });
163
165
  } catch (error) {
164
- this.logger.error(`Failed to list keys for ${tableName}:`, error);
165
- throw new Error(`Failed to list keys: ${error.message}`);
166
- }
167
- }
168
- async createNamespaceById(title) {
169
- if (this.bindings) {
170
- return {
171
- id: title,
172
- // Use title as ID since that's what we need
173
- title,
174
- supports_url_encoding: true
175
- };
166
+ const message = error instanceof Error ? error.message : String(error);
167
+ this.logger?.error(`Error getting message key for thread ${threadId} and message ${messageId}:`, { message });
168
+ throw error;
176
169
  }
177
- return await this.client.kv.namespaces.create({
178
- account_id: this.accountId,
179
- title
180
- });
181
170
  }
182
- async getNamespaceIdByName(namespaceName) {
171
+ getThreadMessagesKey(threadId) {
183
172
  try {
184
- const response = await this.listNamespaces();
185
- const namespace = response.result.find((ns) => ns.title === namespaceName);
186
- return namespace ? namespace.id : null;
173
+ return this.operations.getKey(TABLE_MESSAGES, { threadId, id: "messages" });
187
174
  } catch (error) {
188
- this.logger.error(`Failed to get namespace ID for ${namespaceName}:`, error);
189
- return null;
175
+ const message = error instanceof Error ? error.message : String(error);
176
+ this.logger?.error(`Error getting thread messages key for thread ${threadId}:`, { message });
177
+ throw error;
190
178
  }
191
179
  }
192
- async createNamespace(namespaceName) {
180
+ async deleteThread({ threadId }) {
193
181
  try {
194
- const response = await this.createNamespaceById(namespaceName);
195
- return response.id;
196
- } catch (error) {
197
- if (error.message && error.message.includes("already exists")) {
198
- const namespaces = await this.listNamespaces();
199
- const namespace = namespaces.result.find((ns) => ns.title === namespaceName);
200
- if (namespace) return namespace.id;
182
+ const thread = await this.getThreadById({ threadId });
183
+ if (!thread) {
184
+ throw new Error(`Thread ${threadId} not found`);
201
185
  }
202
- this.logger.error("Error creating namespace:", error);
203
- throw new Error(`Failed to create namespace ${namespaceName}: ${error.message}`);
204
- }
205
- }
206
- async getOrCreateNamespaceId(namespaceName) {
207
- let namespaceId = await this.getNamespaceIdByName(namespaceName);
208
- if (!namespaceId) {
209
- namespaceId = await this.createNamespace(namespaceName);
186
+ const messageKeys = await this.operations.listKV(TABLE_MESSAGES);
187
+ const threadMessageKeys = messageKeys.filter((key) => key.name.includes(`${TABLE_MESSAGES}:${threadId}:`));
188
+ await Promise.all([
189
+ // Delete message order
190
+ this.operations.deleteKV(TABLE_MESSAGES, this.getThreadMessagesKey(threadId)),
191
+ // Delete all messages
192
+ ...threadMessageKeys.map((key) => this.operations.deleteKV(TABLE_MESSAGES, key.name)),
193
+ // Delete thread
194
+ this.operations.deleteKV(TABLE_THREADS, this.operations.getKey(TABLE_THREADS, { id: threadId }))
195
+ ]);
196
+ } catch (error) {
197
+ throw new MastraError(
198
+ {
199
+ id: "CLOUDFLARE_STORAGE_DELETE_THREAD_FAILED",
200
+ domain: ErrorDomain.STORAGE,
201
+ category: ErrorCategory.THIRD_PARTY,
202
+ details: {
203
+ threadId
204
+ }
205
+ },
206
+ error
207
+ );
210
208
  }
211
- return namespaceId;
212
209
  }
213
- async getNamespaceId(tableName) {
214
- const prefix = this.namespacePrefix ? `${this.namespacePrefix}_` : "";
210
+ async findMessageInAnyThread(messageId) {
215
211
  try {
216
- if (tableName === TABLE_MESSAGES || tableName === TABLE_THREADS) {
217
- return await this.getOrCreateNamespaceId(`${prefix}mastra_threads`);
218
- } else if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
219
- return await this.getOrCreateNamespaceId(`${prefix}mastra_workflows`);
220
- } else {
221
- return await this.getOrCreateNamespaceId(`${prefix}mastra_evals`);
212
+ const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
213
+ const threadKeys = await this.operations.listKV(TABLE_THREADS, { prefix: `${prefix}${TABLE_THREADS}` });
214
+ for (const { name: threadKey } of threadKeys) {
215
+ const threadId = threadKey.split(":").pop();
216
+ if (!threadId || threadId === "messages") continue;
217
+ const messageKey = this.getMessageKey(threadId, messageId);
218
+ const message = await this.operations.getKV(TABLE_MESSAGES, messageKey);
219
+ if (message) {
220
+ return { ...message, threadId };
221
+ }
222
222
  }
223
+ return null;
223
224
  } catch (error) {
224
- this.logger.error("Error fetching namespace ID:", error);
225
- throw new Error(`Failed to fetch namespace ID for table ${tableName}: ${error.message}`);
225
+ this.logger?.error(`Error finding message ${messageId} in any thread:`, error);
226
+ return null;
226
227
  }
227
228
  }
228
229
  /**
229
- * Helper to safely serialize data for KV storage
230
+ * Queue for serializing sorted order updates.
231
+ * Updates the sorted order for a given key. This operation is eventually consistent.
230
232
  */
231
- safeSerialize(data) {
232
- return typeof data === "string" ? data : JSON.stringify(data);
233
+ updateQueue = /* @__PURE__ */ new Map();
234
+ async updateSorting(threadMessages) {
235
+ return threadMessages.map((msg) => ({
236
+ message: msg,
237
+ // Use _index if available, otherwise timestamp, matching Upstash
238
+ score: msg._index !== void 0 ? msg._index : msg.createdAt.getTime()
239
+ })).sort((a, b) => a.score - b.score).map((item) => ({
240
+ id: item.message.id,
241
+ score: item.score
242
+ }));
233
243
  }
234
244
  /**
235
- * Helper to safely parse data from KV storage
245
+ * Updates the sorted order for a given key. This operation is eventually consistent.
246
+ * Note: Operations on the same orderKey are serialized using a queue to prevent
247
+ * concurrent updates from conflicting with each other.
236
248
  */
237
- safeParse(text) {
238
- if (!text) return null;
239
- try {
240
- const data = JSON.parse(text);
241
- if (data && typeof data === "object" && "value" in data) {
242
- if (typeof data.value === "string") {
243
- try {
244
- return JSON.parse(data.value);
245
- } catch {
246
- return data.value;
247
- }
249
+ async updateSortedMessages(orderKey, newEntries) {
250
+ const currentPromise = this.updateQueue.get(orderKey) || Promise.resolve();
251
+ const nextPromise = currentPromise.then(async () => {
252
+ try {
253
+ const currentOrder = await this.getSortedMessages(orderKey);
254
+ const orderMap = new Map(currentOrder.map((entry) => [entry.id, entry]));
255
+ for (const entry of newEntries) {
256
+ orderMap.set(entry.id, entry);
257
+ }
258
+ const updatedOrder = Array.from(orderMap.values()).sort((a, b) => a.score - b.score);
259
+ await this.operations.putKV({
260
+ tableName: TABLE_MESSAGES,
261
+ key: orderKey,
262
+ value: JSON.stringify(updatedOrder)
263
+ });
264
+ } catch (error) {
265
+ const message = error instanceof Error ? error.message : String(error);
266
+ this.logger?.error(`Error updating sorted order for key ${orderKey}:`, { message });
267
+ throw error;
268
+ } finally {
269
+ if (this.updateQueue.get(orderKey) === nextPromise) {
270
+ this.updateQueue.delete(orderKey);
248
271
  }
249
- return null;
250
272
  }
251
- return data;
252
- } catch (error) {
253
- const message = error instanceof Error ? error.message : String(error);
254
- this.logger.error("Failed to parse text:", { message, text });
255
- return null;
256
- }
257
- }
258
- async putKV({
259
- tableName,
260
- key,
261
- value,
262
- metadata
263
- }) {
264
- try {
265
- await this.putNamespaceValue({ tableName, key, value, metadata });
266
- } catch (error) {
267
- this.logger.error(`Failed to put KV value for ${tableName}:${key}:`, error);
268
- throw new Error(`Failed to put KV value: ${error.message}`);
269
- }
273
+ });
274
+ this.updateQueue.set(orderKey, nextPromise);
275
+ return nextPromise;
270
276
  }
271
- async getKV(tableName, key) {
277
+ async getSortedMessages(orderKey) {
278
+ const raw = await this.operations.getKV(TABLE_MESSAGES, orderKey);
279
+ if (!raw) return [];
272
280
  try {
273
- const text = await this.getNamespaceValue(tableName, key);
274
- return this.safeParse(text);
275
- } catch (error) {
276
- this.logger.error(`Failed to get KV value for ${tableName}:${key}:`, error);
277
- throw new Error(`Failed to get KV value: ${error.message}`);
281
+ const arr = JSON.parse(typeof raw === "string" ? raw : JSON.stringify(raw));
282
+ return Array.isArray(arr) ? arr : [];
283
+ } catch (e) {
284
+ this.logger?.error(`Error parsing order data for key ${orderKey}:`, { e });
285
+ return [];
278
286
  }
279
287
  }
280
- async deleteKV(tableName, key) {
288
+ async migrateMessage(messageId, fromThreadId, toThreadId) {
281
289
  try {
282
- await this.deleteNamespaceValue(tableName, key);
290
+ const oldMessageKey = this.getMessageKey(fromThreadId, messageId);
291
+ const message = await this.operations.getKV(TABLE_MESSAGES, oldMessageKey);
292
+ if (!message) return;
293
+ const updatedMessage = {
294
+ ...message,
295
+ threadId: toThreadId
296
+ };
297
+ const newMessageKey = this.getMessageKey(toThreadId, messageId);
298
+ await this.operations.putKV({ tableName: TABLE_MESSAGES, key: newMessageKey, value: updatedMessage });
299
+ const oldOrderKey = this.getThreadMessagesKey(fromThreadId);
300
+ const oldEntries = await this.getSortedMessages(oldOrderKey);
301
+ const filteredEntries = oldEntries.filter((entry) => entry.id !== messageId);
302
+ await this.updateSortedMessages(oldOrderKey, filteredEntries);
303
+ const newOrderKey = this.getThreadMessagesKey(toThreadId);
304
+ const newEntries = await this.getSortedMessages(newOrderKey);
305
+ const newEntry = { id: messageId, score: Date.now() };
306
+ newEntries.push(newEntry);
307
+ await this.updateSortedMessages(newOrderKey, newEntries);
308
+ await this.operations.deleteKV(TABLE_MESSAGES, oldMessageKey);
283
309
  } catch (error) {
284
- this.logger.error(`Failed to delete KV value for ${tableName}:${key}:`, error);
285
- throw new Error(`Failed to delete KV value: ${error.message}`);
310
+ this.logger?.error(`Error migrating message ${messageId} from ${fromThreadId} to ${toThreadId}:`, error);
311
+ throw error;
286
312
  }
287
313
  }
288
- async listKV(tableName, options) {
314
+ async saveMessages(args) {
315
+ const { messages } = args;
316
+ if (!Array.isArray(messages) || messages.length === 0) return { messages: [] };
289
317
  try {
290
- return await this.listNamespaceKeys(tableName, options);
318
+ const validatedMessages = messages.map((message, index) => {
319
+ const errors = [];
320
+ if (!message.id) errors.push("id is required");
321
+ if (!message.threadId) errors.push("threadId is required");
322
+ if (!message.content) errors.push("content is required");
323
+ if (!message.role) errors.push("role is required");
324
+ if (!message.createdAt) errors.push("createdAt is required");
325
+ if (message.resourceId === null || message.resourceId === void 0) errors.push("resourceId is required");
326
+ if (errors.length > 0) {
327
+ throw new Error(`Invalid message at index ${index}: ${errors.join(", ")}`);
328
+ }
329
+ return {
330
+ ...message,
331
+ createdAt: ensureDate(message.createdAt),
332
+ type: message.type || "v2",
333
+ _index: index
334
+ };
335
+ }).filter((m) => !!m);
336
+ const messageMigrationTasks = [];
337
+ for (const message of validatedMessages) {
338
+ const existingMessage = await this.findMessageInAnyThread(message.id);
339
+ this.logger?.debug(
340
+ `Checking message ${message.id}: existing=${existingMessage?.threadId}, new=${message.threadId}`
341
+ );
342
+ if (existingMessage && existingMessage.threadId && existingMessage.threadId !== message.threadId) {
343
+ this.logger?.debug(`Migrating message ${message.id} from ${existingMessage.threadId} to ${message.threadId}`);
344
+ messageMigrationTasks.push(this.migrateMessage(message.id, existingMessage.threadId, message.threadId));
345
+ }
346
+ }
347
+ await Promise.all(messageMigrationTasks);
348
+ const messagesByThread = validatedMessages.reduce((acc, message) => {
349
+ if (message.threadId && !acc.has(message.threadId)) {
350
+ acc.set(message.threadId, []);
351
+ }
352
+ if (message.threadId) {
353
+ acc.get(message.threadId).push(message);
354
+ }
355
+ return acc;
356
+ }, /* @__PURE__ */ new Map());
357
+ await Promise.all(
358
+ Array.from(messagesByThread.entries()).map(async ([threadId, threadMessages]) => {
359
+ try {
360
+ const thread = await this.getThreadById({ threadId });
361
+ if (!thread) {
362
+ throw new Error(`Thread ${threadId} not found`);
363
+ }
364
+ await Promise.all(
365
+ threadMessages.map(async (message) => {
366
+ const key = this.getMessageKey(threadId, message.id);
367
+ const { _index, ...cleanMessage } = message;
368
+ const serializedMessage = {
369
+ ...cleanMessage,
370
+ createdAt: serializeDate(cleanMessage.createdAt)
371
+ };
372
+ this.logger?.debug(`Saving message ${message.id}`, {
373
+ contentSummary: this.summarizeMessageContent(serializedMessage.content)
374
+ });
375
+ await this.operations.putKV({ tableName: TABLE_MESSAGES, key, value: serializedMessage });
376
+ })
377
+ );
378
+ const orderKey = this.getThreadMessagesKey(threadId);
379
+ const entries = await this.updateSorting(threadMessages);
380
+ await this.updateSortedMessages(orderKey, entries);
381
+ const updatedThread = {
382
+ ...thread,
383
+ updatedAt: /* @__PURE__ */ new Date()
384
+ };
385
+ await this.operations.putKV({
386
+ tableName: TABLE_THREADS,
387
+ key: this.operations.getKey(TABLE_THREADS, { id: threadId }),
388
+ value: updatedThread
389
+ });
390
+ } catch (error) {
391
+ throw new MastraError(
392
+ {
393
+ id: "CLOUDFLARE_STORAGE_SAVE_MESSAGES_FAILED",
394
+ domain: ErrorDomain.STORAGE,
395
+ category: ErrorCategory.THIRD_PARTY,
396
+ details: {
397
+ threadId
398
+ }
399
+ },
400
+ error
401
+ );
402
+ }
403
+ })
404
+ );
405
+ const prepared = validatedMessages.map(
406
+ ({ _index, ...message }) => ({ ...message, type: message.type !== "v2" ? message.type : void 0 })
407
+ );
408
+ const list = new MessageList().add(prepared, "memory");
409
+ return { messages: list.get.all.db() };
291
410
  } catch (error) {
292
- this.logger.error(`Failed to list KV for ${tableName}:`, error);
293
- throw new Error(`Failed to list KV: ${error.message}`);
411
+ throw new MastraError(
412
+ {
413
+ id: "CLOUDFLARE_STORAGE_SAVE_MESSAGES_FAILED",
414
+ domain: ErrorDomain.STORAGE,
415
+ category: ErrorCategory.THIRD_PARTY
416
+ },
417
+ error
418
+ );
294
419
  }
295
420
  }
296
- /*---------------------------------------------------------------------------
297
- Sorted set simulation helpers for message ordering.
298
- We store an array of objects { id, score } as JSON under a dedicated key.
299
- ---------------------------------------------------------------------------*/
300
- async getSortedMessages(orderKey) {
301
- const raw = await this.getKV(TABLE_MESSAGES, orderKey);
302
- if (!raw) return [];
303
- try {
304
- const arr = JSON.parse(typeof raw === "string" ? raw : JSON.stringify(raw));
305
- return Array.isArray(arr) ? arr : [];
306
- } catch (e) {
307
- this.logger.error(`Error parsing order data for key ${orderKey}:`, { e });
308
- return [];
309
- }
421
+ async getRank(orderKey, id) {
422
+ const order = await this.getSortedMessages(orderKey);
423
+ const index = order.findIndex((item) => item.id === id);
424
+ return index >= 0 ? index : null;
310
425
  }
311
- async updateSorting(threadMessages) {
312
- return threadMessages.map((msg) => ({
313
- message: msg,
314
- // Use _index if available, otherwise timestamp, matching Upstash
315
- score: msg._index !== void 0 ? msg._index : msg.createdAt.getTime()
316
- })).sort((a, b) => a.score - b.score).map((item) => ({
317
- id: item.message.id,
318
- score: item.score
319
- }));
426
+ async getRange(orderKey, start, end) {
427
+ const order = await this.getSortedMessages(orderKey);
428
+ const actualStart = start < 0 ? Math.max(0, order.length + start) : start;
429
+ const actualEnd = end < 0 ? order.length + end : Math.min(end, order.length - 1);
430
+ const sliced = order.slice(actualStart, actualEnd + 1);
431
+ return sliced.map((item) => item.id);
432
+ }
433
+ async getLastN(orderKey, n) {
434
+ return this.getRange(orderKey, -n, -1);
435
+ }
436
+ async getFullOrder(orderKey) {
437
+ return this.getRange(orderKey, 0, -1);
320
438
  }
321
439
  async getIncludedMessagesWithContext(threadId, include, messageIds) {
322
- const threadMessagesKey = this.getThreadMessagesKey(threadId);
323
440
  await Promise.all(
324
441
  include.map(async (item) => {
442
+ const targetThreadId = item.threadId || threadId;
443
+ if (!targetThreadId) return;
444
+ const threadMessagesKey = this.getThreadMessagesKey(targetThreadId);
325
445
  messageIds.add(item.id);
326
446
  if (!item.withPreviousMessages && !item.withNextMessages) return;
327
447
  const rank = await this.getRank(threadMessagesKey, item.id);
@@ -342,87 +462,560 @@ var CloudflareStore = class extends MastraStorage {
342
462
  );
343
463
  }
344
464
  async getRecentMessages(threadId, limit, messageIds) {
465
+ if (!threadId.trim()) throw new Error("threadId must be a non-empty string");
345
466
  if (limit <= 0) return;
346
467
  try {
347
468
  const threadMessagesKey = this.getThreadMessagesKey(threadId);
348
469
  const latestIds = await this.getLastN(threadMessagesKey, limit);
349
470
  latestIds.forEach((id) => messageIds.add(id));
350
471
  } catch {
351
- console.log(`No message order found for thread ${threadId}, skipping latest messages`);
472
+ this.logger?.debug(`No message order found for thread ${threadId}, skipping latest messages`);
352
473
  }
353
474
  }
354
- async fetchAndParseMessages(threadId, messageIds) {
475
+ async fetchAndParseMessagesFromMultipleThreads(messageIds, include, targetThreadId) {
476
+ const messageIdToThreadId = /* @__PURE__ */ new Map();
477
+ if (include) {
478
+ for (const item of include) {
479
+ if (item.threadId) {
480
+ messageIdToThreadId.set(item.id, item.threadId);
481
+ }
482
+ }
483
+ }
355
484
  const messages = await Promise.all(
356
485
  messageIds.map(async (id) => {
357
486
  try {
487
+ let threadId = messageIdToThreadId.get(id);
488
+ if (!threadId) {
489
+ if (targetThreadId) {
490
+ threadId = targetThreadId;
491
+ } else {
492
+ const foundMessage = await this.findMessageInAnyThread(id);
493
+ if (foundMessage) {
494
+ threadId = foundMessage.threadId;
495
+ }
496
+ }
497
+ }
498
+ if (!threadId) return null;
358
499
  const key = this.getMessageKey(threadId, id);
359
- const data = await this.getKV(TABLE_MESSAGES, key);
500
+ const data = await this.operations.getKV(TABLE_MESSAGES, key);
360
501
  if (!data) return null;
361
- return typeof data === "string" ? JSON.parse(data) : data;
502
+ const parsed = typeof data === "string" ? JSON.parse(data) : data;
503
+ this.logger?.debug(`Retrieved message ${id} from thread ${threadId}`, {
504
+ contentSummary: this.summarizeMessageContent(parsed.content)
505
+ });
506
+ return parsed;
362
507
  } catch (error) {
363
508
  const message = error instanceof Error ? error.message : String(error);
364
- this.logger.error(`Error retrieving message ${id}:`, { message });
509
+ this.logger?.error(`Error retrieving message ${id}:`, { message });
365
510
  return null;
366
511
  }
367
512
  })
368
513
  );
369
514
  return messages.filter((msg) => msg !== null);
370
515
  }
371
- /**
372
- * Queue for serializing sorted order updates.
373
- * Updates the sorted order for a given key. This operation is eventually consistent.
374
- */
375
- updateQueue = /* @__PURE__ */ new Map();
376
- /**
377
- * Updates the sorted order for a given key. This operation is eventually consistent.
378
- * Note: Operations on the same orderKey are serialized using a queue to prevent
379
- * concurrent updates from conflicting with each other.
380
- */
381
- async updateSortedMessages(orderKey, newEntries) {
382
- const currentPromise = this.updateQueue.get(orderKey) || Promise.resolve();
383
- const nextPromise = currentPromise.then(async () => {
384
- try {
385
- const currentOrder = await this.getSortedMessages(orderKey);
386
- const orderMap = new Map(currentOrder.map((entry) => [entry.id, entry]));
387
- for (const entry of newEntries) {
388
- orderMap.set(entry.id, entry);
516
+ async listMessagesById({ messageIds }) {
517
+ if (messageIds.length === 0) return { messages: [] };
518
+ try {
519
+ const messages = (await Promise.all(messageIds.map((id) => this.findMessageInAnyThread(id)))).filter(
520
+ (result) => !!result
521
+ );
522
+ const prepared = messages.map(({ _index, ...message }) => ({
523
+ ...message,
524
+ ...message.type !== `v2` && { type: message.type },
525
+ createdAt: ensureDate(message.createdAt)
526
+ }));
527
+ const list = new MessageList().add(prepared, "memory");
528
+ return { messages: list.get.all.db() };
529
+ } catch (error) {
530
+ const mastraError = new MastraError(
531
+ {
532
+ id: "CLOUDFLARE_STORAGE_LIST_MESSAGES_BY_ID_FAILED",
533
+ domain: ErrorDomain.STORAGE,
534
+ category: ErrorCategory.THIRD_PARTY,
535
+ text: `Error retrieving messages by ID`,
536
+ details: {
537
+ messageIds: JSON.stringify(messageIds)
538
+ }
539
+ },
540
+ error
541
+ );
542
+ this.logger?.trackException(mastraError);
543
+ this.logger?.error(mastraError.toString());
544
+ return { messages: [] };
545
+ }
546
+ }
547
+ async listMessages(args) {
548
+ const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
549
+ if (!threadId.trim()) {
550
+ throw new MastraError(
551
+ {
552
+ id: "STORAGE_CLOUDFLARE_LIST_MESSAGES_INVALID_THREAD_ID",
553
+ domain: ErrorDomain.STORAGE,
554
+ category: ErrorCategory.THIRD_PARTY,
555
+ details: { threadId }
556
+ },
557
+ new Error("threadId must be a non-empty string")
558
+ );
559
+ }
560
+ const perPage = normalizePerPage(perPageInput, 40);
561
+ const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
562
+ try {
563
+ if (page < 0) {
564
+ throw new MastraError(
565
+ {
566
+ id: "STORAGE_CLOUDFLARE_LIST_MESSAGES_INVALID_PAGE",
567
+ domain: ErrorDomain.STORAGE,
568
+ category: ErrorCategory.USER,
569
+ details: { page }
570
+ },
571
+ new Error("page must be >= 0")
572
+ );
573
+ }
574
+ const { field, direction } = this.parseOrderBy(orderBy, "ASC");
575
+ const messageIds = /* @__PURE__ */ new Set();
576
+ const hasFilters = !!resourceId || !!filter?.dateRange;
577
+ if (hasFilters || perPage === Number.MAX_SAFE_INTEGER) {
578
+ try {
579
+ const threadMessagesKey = this.getThreadMessagesKey(threadId);
580
+ const allIds = await this.getFullOrder(threadMessagesKey);
581
+ allIds.forEach((id) => messageIds.add(id));
582
+ } catch {
389
583
  }
390
- const updatedOrder = Array.from(orderMap.values()).sort((a, b) => a.score - b.score);
391
- await this.putKV({
392
- tableName: TABLE_MESSAGES,
393
- key: orderKey,
394
- value: JSON.stringify(updatedOrder)
584
+ } else {
585
+ if (perPage > 0) {
586
+ try {
587
+ const threadMessagesKey = this.getThreadMessagesKey(threadId);
588
+ const fullOrder = await this.getFullOrder(threadMessagesKey);
589
+ const totalMessages = fullOrder.length;
590
+ let start;
591
+ let end;
592
+ if (direction === "ASC") {
593
+ start = offset;
594
+ end = Math.min(offset + perPage - 1, totalMessages - 1);
595
+ } else {
596
+ start = Math.max(totalMessages - offset - perPage, 0);
597
+ end = totalMessages - offset - 1;
598
+ }
599
+ const paginatedIds = await this.getRange(threadMessagesKey, start, end);
600
+ paginatedIds.forEach((id) => messageIds.add(id));
601
+ } catch {
602
+ }
603
+ }
604
+ }
605
+ if (include && include.length > 0) {
606
+ await this.getIncludedMessagesWithContext(threadId, include, messageIds);
607
+ }
608
+ const messages = await this.fetchAndParseMessagesFromMultipleThreads(
609
+ Array.from(messageIds),
610
+ include,
611
+ include && include.length > 0 ? void 0 : threadId
612
+ );
613
+ let filteredMessages = messages;
614
+ if (resourceId) {
615
+ filteredMessages = filteredMessages.filter((msg) => msg.resourceId === resourceId);
616
+ }
617
+ const dateRange = filter?.dateRange;
618
+ if (dateRange) {
619
+ filteredMessages = filteredMessages.filter((msg) => {
620
+ const messageDate = new Date(msg.createdAt);
621
+ if (dateRange.start && messageDate < new Date(dateRange.start)) return false;
622
+ if (dateRange.end && messageDate > new Date(dateRange.end)) return false;
623
+ return true;
395
624
  });
396
- } catch (error) {
397
- const message = error instanceof Error ? error.message : String(error);
398
- this.logger.error(`Error updating sorted order for key ${orderKey}:`, { message });
399
- throw error;
400
- } finally {
401
- if (this.updateQueue.get(orderKey) === nextPromise) {
402
- this.updateQueue.delete(orderKey);
625
+ }
626
+ let total;
627
+ if (hasFilters) {
628
+ total = filteredMessages.length;
629
+ } else {
630
+ try {
631
+ const threadMessagesKey = this.getThreadMessagesKey(threadId);
632
+ const fullOrder = await this.getFullOrder(threadMessagesKey);
633
+ total = fullOrder.length;
634
+ } catch {
635
+ total = filteredMessages.length;
403
636
  }
404
637
  }
405
- });
406
- this.updateQueue.set(orderKey, nextPromise);
407
- return nextPromise;
638
+ if (perPage === 0 && (!include || include.length === 0)) {
639
+ return {
640
+ messages: [],
641
+ total,
642
+ page,
643
+ perPage: perPageForResponse,
644
+ hasMore: offset < total
645
+ };
646
+ }
647
+ if (hasFilters && perPage !== Number.MAX_SAFE_INTEGER && perPage > 0) {
648
+ if (direction === "ASC") {
649
+ filteredMessages = filteredMessages.slice(offset, offset + perPage);
650
+ } else {
651
+ const start = Math.max(filteredMessages.length - offset - perPage, 0);
652
+ const end = filteredMessages.length - offset;
653
+ filteredMessages = filteredMessages.slice(start, end);
654
+ }
655
+ }
656
+ const paginatedCount = hasFilters && perPage !== Number.MAX_SAFE_INTEGER && perPage > 0 ? filteredMessages.length : filteredMessages.length;
657
+ try {
658
+ const threadMessagesKey = this.getThreadMessagesKey(threadId);
659
+ const messageOrder = await this.getFullOrder(threadMessagesKey);
660
+ const orderMap = new Map(messageOrder.map((id, index) => [id, index]));
661
+ filteredMessages.sort((a, b) => {
662
+ const indexA = orderMap.get(a.id);
663
+ const indexB = orderMap.get(b.id);
664
+ if (indexA !== void 0 && indexB !== void 0) {
665
+ return direction === "ASC" ? indexA - indexB : indexB - indexA;
666
+ }
667
+ const timeA = new Date(a.createdAt).getTime();
668
+ const timeB = new Date(b.createdAt).getTime();
669
+ const timeDiff = direction === "ASC" ? timeA - timeB : timeB - timeA;
670
+ if (timeDiff === 0) {
671
+ return a.id.localeCompare(b.id);
672
+ }
673
+ return timeDiff;
674
+ });
675
+ } catch {
676
+ filteredMessages.sort((a, b) => {
677
+ const timeA = new Date(a.createdAt).getTime();
678
+ const timeB = new Date(b.createdAt).getTime();
679
+ const timeDiff = direction === "ASC" ? timeA - timeB : timeB - timeA;
680
+ if (timeDiff === 0) {
681
+ return a.id.localeCompare(b.id);
682
+ }
683
+ return timeDiff;
684
+ });
685
+ }
686
+ if (total === 0 && filteredMessages.length === 0 && (!include || include.length === 0)) {
687
+ return {
688
+ messages: [],
689
+ total: 0,
690
+ page,
691
+ perPage: perPageForResponse,
692
+ hasMore: false
693
+ };
694
+ }
695
+ const prepared = filteredMessages.map(({ _index, ...message }) => ({
696
+ ...message,
697
+ type: message.type !== "v2" ? message.type : void 0,
698
+ createdAt: ensureDate(message.createdAt)
699
+ }));
700
+ const list = new MessageList({ threadId, resourceId }).add(prepared, "memory");
701
+ let finalMessages = list.get.all.db();
702
+ finalMessages = finalMessages.sort((a, b) => {
703
+ const isDateField = field === "createdAt" || field === "updatedAt";
704
+ const aVal = isDateField ? new Date(a[field]).getTime() : a[field];
705
+ const bVal = isDateField ? new Date(b[field]).getTime() : b[field];
706
+ if (aVal == null && bVal == null) return a.id.localeCompare(b.id);
707
+ if (aVal == null) return 1;
708
+ if (bVal == null) return -1;
709
+ if (typeof aVal === "number" && typeof bVal === "number") {
710
+ const cmp2 = direction === "ASC" ? aVal - bVal : bVal - aVal;
711
+ return cmp2 !== 0 ? cmp2 : a.id.localeCompare(b.id);
712
+ }
713
+ const cmp = direction === "ASC" ? String(aVal).localeCompare(String(bVal)) : String(bVal).localeCompare(String(aVal));
714
+ return cmp !== 0 ? cmp : a.id.localeCompare(b.id);
715
+ });
716
+ const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
717
+ const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
718
+ let hasMore;
719
+ if (perPageInput === false || allThreadMessagesReturned) {
720
+ hasMore = false;
721
+ } else if (direction === "ASC") {
722
+ hasMore = offset + paginatedCount < total;
723
+ } else {
724
+ hasMore = total - offset - perPage > 0;
725
+ }
726
+ return {
727
+ messages: finalMessages,
728
+ total,
729
+ page,
730
+ perPage: perPageForResponse,
731
+ hasMore
732
+ };
733
+ } catch (error) {
734
+ const mastraError = new MastraError(
735
+ {
736
+ id: "CLOUDFLARE_STORAGE_LIST_MESSAGES_FAILED",
737
+ domain: ErrorDomain.STORAGE,
738
+ category: ErrorCategory.THIRD_PARTY,
739
+ text: `Failed to list messages for thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
740
+ details: {
741
+ threadId,
742
+ resourceId: resourceId ?? ""
743
+ }
744
+ },
745
+ error
746
+ );
747
+ this.logger?.error?.(mastraError.toString());
748
+ this.logger?.trackException?.(mastraError);
749
+ return {
750
+ messages: [],
751
+ total: 0,
752
+ page,
753
+ perPage: perPageForResponse,
754
+ hasMore: false
755
+ };
756
+ }
757
+ }
758
+ async updateMessages(args) {
759
+ try {
760
+ const { messages } = args;
761
+ const updatedMessages = [];
762
+ for (const messageUpdate of messages) {
763
+ const { id, content, ...otherFields } = messageUpdate;
764
+ const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
765
+ const keyObjs = await this.operations.listKV(TABLE_MESSAGES, { prefix: `${prefix}${TABLE_MESSAGES}` });
766
+ let existingMessage = null;
767
+ let messageKey = "";
768
+ for (const { name: key } of keyObjs) {
769
+ const data = await this.operations.getKV(TABLE_MESSAGES, key);
770
+ if (data && data.id === id) {
771
+ existingMessage = data;
772
+ messageKey = key;
773
+ break;
774
+ }
775
+ }
776
+ if (!existingMessage) {
777
+ continue;
778
+ }
779
+ const updatedMessage = {
780
+ ...existingMessage,
781
+ ...otherFields,
782
+ id
783
+ };
784
+ if (content) {
785
+ if (content.metadata !== void 0) {
786
+ updatedMessage.content = {
787
+ ...updatedMessage.content,
788
+ metadata: {
789
+ ...updatedMessage.content?.metadata,
790
+ ...content.metadata
791
+ }
792
+ };
793
+ }
794
+ if (content.content !== void 0) {
795
+ updatedMessage.content = {
796
+ ...updatedMessage.content,
797
+ content: content.content
798
+ };
799
+ }
800
+ }
801
+ if ("threadId" in messageUpdate && messageUpdate.threadId && messageUpdate.threadId !== existingMessage.threadId) {
802
+ await this.operations.deleteKV(TABLE_MESSAGES, messageKey);
803
+ updatedMessage.threadId = messageUpdate.threadId;
804
+ const newMessageKey = this.getMessageKey(messageUpdate.threadId, id);
805
+ await this.operations.putKV({
806
+ tableName: TABLE_MESSAGES,
807
+ key: newMessageKey,
808
+ value: updatedMessage
809
+ });
810
+ if (existingMessage.threadId) {
811
+ const sourceOrderKey = this.getThreadMessagesKey(existingMessage.threadId);
812
+ const sourceEntries = await this.getSortedMessages(sourceOrderKey);
813
+ const filteredEntries = sourceEntries.filter((entry) => entry.id !== id);
814
+ await this.updateSortedMessages(sourceOrderKey, filteredEntries);
815
+ }
816
+ const destOrderKey = this.getThreadMessagesKey(messageUpdate.threadId);
817
+ const destEntries = await this.getSortedMessages(destOrderKey);
818
+ const newEntry = { id, score: Date.now() };
819
+ destEntries.push(newEntry);
820
+ await this.updateSortedMessages(destOrderKey, destEntries);
821
+ } else {
822
+ await this.operations.putKV({
823
+ tableName: TABLE_MESSAGES,
824
+ key: messageKey,
825
+ value: updatedMessage
826
+ });
827
+ }
828
+ const threadsToUpdate = /* @__PURE__ */ new Set();
829
+ if (updatedMessage.threadId) {
830
+ threadsToUpdate.add(updatedMessage.threadId);
831
+ }
832
+ if ("threadId" in messageUpdate && messageUpdate.threadId && messageUpdate.threadId !== existingMessage.threadId) {
833
+ if (existingMessage.threadId) {
834
+ threadsToUpdate.add(existingMessage.threadId);
835
+ }
836
+ threadsToUpdate.add(messageUpdate.threadId);
837
+ }
838
+ for (const threadId of threadsToUpdate) {
839
+ const thread = await this.getThreadById({ threadId });
840
+ if (thread) {
841
+ const updatedThread = {
842
+ ...thread,
843
+ updatedAt: /* @__PURE__ */ new Date()
844
+ };
845
+ await this.operations.putKV({
846
+ tableName: TABLE_THREADS,
847
+ key: this.operations.getKey(TABLE_THREADS, { id: threadId }),
848
+ value: updatedThread
849
+ });
850
+ }
851
+ }
852
+ updatedMessages.push(updatedMessage);
853
+ }
854
+ return updatedMessages;
855
+ } catch (error) {
856
+ throw new MastraError(
857
+ {
858
+ id: "CLOUDFLARE_STORAGE_UPDATE_MESSAGES_FAILED",
859
+ domain: ErrorDomain.STORAGE,
860
+ category: ErrorCategory.THIRD_PARTY,
861
+ text: "Failed to update messages"
862
+ },
863
+ error
864
+ );
865
+ }
866
+ }
867
+ async getResourceById({ resourceId }) {
868
+ try {
869
+ const data = await this.operations.getKV(TABLE_RESOURCES, resourceId);
870
+ if (!data) return null;
871
+ const resource = typeof data === "string" ? JSON.parse(data) : data;
872
+ return {
873
+ ...resource,
874
+ createdAt: ensureDate(resource.createdAt),
875
+ updatedAt: ensureDate(resource.updatedAt),
876
+ metadata: this.ensureMetadata(resource.metadata)
877
+ };
878
+ } catch (error) {
879
+ const mastraError = new MastraError(
880
+ {
881
+ id: "CLOUDFLARE_STORAGE_GET_RESOURCE_BY_ID_FAILED",
882
+ domain: ErrorDomain.STORAGE,
883
+ category: ErrorCategory.THIRD_PARTY,
884
+ details: {
885
+ resourceId
886
+ }
887
+ },
888
+ error
889
+ );
890
+ this.logger?.trackException(mastraError);
891
+ this.logger?.error(mastraError.toString());
892
+ return null;
893
+ }
894
+ }
895
+ async saveResource({ resource }) {
896
+ try {
897
+ const resourceToSave = {
898
+ ...resource,
899
+ metadata: resource.metadata ? JSON.stringify(resource.metadata) : null
900
+ };
901
+ await this.operations.putKV({
902
+ tableName: TABLE_RESOURCES,
903
+ key: resource.id,
904
+ value: resourceToSave
905
+ });
906
+ return resource;
907
+ } catch (error) {
908
+ throw new MastraError(
909
+ {
910
+ id: "CLOUDFLARE_STORAGE_SAVE_RESOURCE_FAILED",
911
+ domain: ErrorDomain.STORAGE,
912
+ category: ErrorCategory.THIRD_PARTY,
913
+ details: {
914
+ resourceId: resource.id
915
+ }
916
+ },
917
+ error
918
+ );
919
+ }
920
+ }
921
+ async updateResource({
922
+ resourceId,
923
+ workingMemory,
924
+ metadata
925
+ }) {
926
+ const existingResource = await this.getResourceById({ resourceId });
927
+ if (!existingResource) {
928
+ const newResource = {
929
+ id: resourceId,
930
+ workingMemory,
931
+ metadata: metadata || {},
932
+ createdAt: /* @__PURE__ */ new Date(),
933
+ updatedAt: /* @__PURE__ */ new Date()
934
+ };
935
+ return this.saveResource({ resource: newResource });
936
+ }
937
+ const updatedAt = /* @__PURE__ */ new Date();
938
+ const updatedResource = {
939
+ ...existingResource,
940
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
941
+ metadata: {
942
+ ...existingResource.metadata,
943
+ ...metadata
944
+ },
945
+ updatedAt
946
+ };
947
+ return this.saveResource({ resource: updatedResource });
948
+ }
949
+ };
950
+ var StoreOperationsCloudflare = class extends StoreOperations {
951
+ bindings;
952
+ client;
953
+ accountId;
954
+ namespacePrefix;
955
+ constructor({
956
+ namespacePrefix,
957
+ bindings,
958
+ client,
959
+ accountId
960
+ }) {
961
+ super();
962
+ this.bindings = bindings;
963
+ this.namespacePrefix = namespacePrefix;
964
+ this.client = client;
965
+ this.accountId = accountId;
408
966
  }
409
- async getRank(orderKey, id) {
410
- const order = await this.getSortedMessages(orderKey);
411
- const index = order.findIndex((item) => item.id === id);
412
- return index >= 0 ? index : null;
967
+ async hasColumn() {
968
+ return true;
413
969
  }
414
- async getRange(orderKey, start, end) {
415
- const order = await this.getSortedMessages(orderKey);
416
- const actualStart = start < 0 ? Math.max(0, order.length + start) : start;
417
- const actualEnd = end < 0 ? order.length + end : Math.min(end, order.length - 1);
418
- const sliced = order.slice(actualStart, actualEnd + 1);
419
- return sliced.map((item) => item.id);
970
+ async alterTable(_args) {
420
971
  }
421
- async getLastN(orderKey, n) {
422
- return this.getRange(orderKey, -n, -1);
972
+ async clearTable({ tableName }) {
973
+ try {
974
+ const keys = await this.listKV(tableName);
975
+ if (keys.length > 0) {
976
+ await Promise.all(keys.map((keyObj) => this.deleteKV(tableName, keyObj.name)));
977
+ }
978
+ } catch (error) {
979
+ throw new MastraError(
980
+ {
981
+ id: "CLOUDFLARE_STORAGE_CLEAR_TABLE_FAILED",
982
+ domain: ErrorDomain.STORAGE,
983
+ category: ErrorCategory.THIRD_PARTY,
984
+ details: {
985
+ tableName
986
+ }
987
+ },
988
+ error
989
+ );
990
+ }
423
991
  }
424
- async getFullOrder(orderKey) {
425
- return this.getRange(orderKey, 0, -1);
992
+ async dropTable({ tableName }) {
993
+ try {
994
+ const keys = await this.listKV(tableName);
995
+ if (keys.length > 0) {
996
+ await Promise.all(keys.map((keyObj) => this.deleteKV(tableName, keyObj.name)));
997
+ }
998
+ } catch (error) {
999
+ throw new MastraError(
1000
+ {
1001
+ id: "CLOUDFLARE_STORAGE_DROP_TABLE_FAILED",
1002
+ domain: ErrorDomain.STORAGE,
1003
+ category: ErrorCategory.THIRD_PARTY,
1004
+ details: {
1005
+ tableName
1006
+ }
1007
+ },
1008
+ error
1009
+ );
1010
+ }
1011
+ }
1012
+ getBinding(tableName) {
1013
+ if (!this.bindings) {
1014
+ throw new Error(`Cannot use Workers API binding for ${tableName}: Store initialized with REST API configuration`);
1015
+ }
1016
+ const binding = this.bindings[tableName];
1017
+ if (!binding) throw new Error(`No binding found for namespace ${tableName}`);
1018
+ return binding;
426
1019
  }
427
1020
  getKey(tableName, record) {
428
1021
  const prefix = this.namespacePrefix ? `${this.namespacePrefix}:` : "";
@@ -434,10 +1027,10 @@ var CloudflareStore = class extends MastraStorage {
434
1027
  if (!record.threadId || !record.id) throw new Error("Thread ID and Message ID are required");
435
1028
  return `${prefix}${tableName}:${record.threadId}:${record.id}`;
436
1029
  case TABLE_WORKFLOW_SNAPSHOT:
437
- if (!record.namespace || !record.workflow_name || !record.run_id) {
438
- throw new Error("Namespace, workflow name, and run ID are required");
1030
+ if (!record.workflow_name || !record.run_id) {
1031
+ throw new Error("Workflow name, and run ID are required");
439
1032
  }
440
- let key = `${prefix}${tableName}:${record.namespace}:${record.workflow_name}:${record.run_id}`;
1033
+ let key = `${prefix}${tableName}:${record.workflow_name}:${record.run_id}`;
441
1034
  if (record.resourceId) {
442
1035
  key = `${key}:${record.resourceId}`;
443
1036
  }
@@ -445,6 +1038,9 @@ var CloudflareStore = class extends MastraStorage {
445
1038
  case TABLE_TRACES:
446
1039
  if (!record.id) throw new Error("Trace ID is required");
447
1040
  return `${prefix}${tableName}:${record.id}`;
1041
+ case TABLE_SCORERS:
1042
+ if (!record.id) throw new Error("Score ID is required");
1043
+ return `${prefix}${tableName}:${record.id}`;
448
1044
  default:
449
1045
  throw new Error(`Unsupported table: ${tableName}`);
450
1046
  }
@@ -453,6 +1049,146 @@ var CloudflareStore = class extends MastraStorage {
453
1049
  const prefix = this.namespacePrefix ? `${this.namespacePrefix}:` : "";
454
1050
  return `${prefix}schema:${tableName}`;
455
1051
  }
1052
+ /**
1053
+ * Helper to safely parse data from KV storage
1054
+ */
1055
+ safeParse(text) {
1056
+ if (!text) return null;
1057
+ try {
1058
+ const data = JSON.parse(text);
1059
+ if (data && typeof data === "object" && "value" in data) {
1060
+ if (typeof data.value === "string") {
1061
+ try {
1062
+ return JSON.parse(data.value);
1063
+ } catch {
1064
+ return data.value;
1065
+ }
1066
+ }
1067
+ return null;
1068
+ }
1069
+ return data;
1070
+ } catch (error) {
1071
+ const message = error instanceof Error ? error.message : String(error);
1072
+ this.logger.error("Failed to parse text:", { message, text });
1073
+ return null;
1074
+ }
1075
+ }
1076
+ async createNamespaceById(title) {
1077
+ if (this.bindings) {
1078
+ return {
1079
+ id: title,
1080
+ // Use title as ID since that's what we need
1081
+ title,
1082
+ supports_url_encoding: true
1083
+ };
1084
+ }
1085
+ return await this.client.kv.namespaces.create({
1086
+ account_id: this.accountId,
1087
+ title
1088
+ });
1089
+ }
1090
+ async createNamespace(namespaceName) {
1091
+ try {
1092
+ const response = await this.createNamespaceById(namespaceName);
1093
+ return response.id;
1094
+ } catch (error) {
1095
+ if (error.message && error.message.includes("already exists")) {
1096
+ const namespaces = await this.listNamespaces();
1097
+ const namespace = namespaces.result.find((ns) => ns.title === namespaceName);
1098
+ if (namespace) return namespace.id;
1099
+ }
1100
+ this.logger.error("Error creating namespace:", error);
1101
+ throw new Error(`Failed to create namespace ${namespaceName}: ${error.message}`);
1102
+ }
1103
+ }
1104
+ async listNamespaces() {
1105
+ if (this.bindings) {
1106
+ return {
1107
+ result: Object.keys(this.bindings).map((name) => ({
1108
+ id: name,
1109
+ title: name,
1110
+ supports_url_encoding: true
1111
+ }))
1112
+ };
1113
+ }
1114
+ let allNamespaces = [];
1115
+ let currentPage = 1;
1116
+ const perPage = 50;
1117
+ let morePagesExist = true;
1118
+ while (morePagesExist) {
1119
+ const response = await this.client.kv.namespaces.list({
1120
+ account_id: this.accountId,
1121
+ page: currentPage,
1122
+ per_page: perPage
1123
+ });
1124
+ if (response.result) {
1125
+ allNamespaces = allNamespaces.concat(response.result);
1126
+ }
1127
+ morePagesExist = response.result ? response.result.length === perPage : false;
1128
+ if (morePagesExist) {
1129
+ currentPage++;
1130
+ }
1131
+ }
1132
+ return { result: allNamespaces };
1133
+ }
1134
+ async getNamespaceIdByName(namespaceName) {
1135
+ try {
1136
+ const response = await this.listNamespaces();
1137
+ const namespace = response.result.find((ns) => ns.title === namespaceName);
1138
+ return namespace ? namespace.id : null;
1139
+ } catch (error) {
1140
+ this.logger.error(`Failed to get namespace ID for ${namespaceName}:`, error);
1141
+ return null;
1142
+ }
1143
+ }
1144
+ async getOrCreateNamespaceId(namespaceName) {
1145
+ let namespaceId = await this.getNamespaceIdByName(namespaceName);
1146
+ if (!namespaceId) {
1147
+ namespaceId = await this.createNamespace(namespaceName);
1148
+ }
1149
+ return namespaceId;
1150
+ }
1151
+ async getNamespaceId(tableName) {
1152
+ const prefix = this.namespacePrefix ? `${this.namespacePrefix}_` : "";
1153
+ try {
1154
+ return await this.getOrCreateNamespaceId(`${prefix}${tableName}`);
1155
+ } catch (error) {
1156
+ this.logger.error("Error fetching namespace ID:", error);
1157
+ throw new Error(`Failed to fetch namespace ID for table ${tableName}: ${error.message}`);
1158
+ }
1159
+ }
1160
+ async getNamespaceValue(tableName, key) {
1161
+ try {
1162
+ if (this.bindings) {
1163
+ const binding = this.getBinding(tableName);
1164
+ const result = await binding.getWithMetadata(key, "text");
1165
+ if (!result) return null;
1166
+ return JSON.stringify(result);
1167
+ } else {
1168
+ const namespaceId = await this.getNamespaceId(tableName);
1169
+ const response = await this.client.kv.namespaces.values.get(namespaceId, key, {
1170
+ account_id: this.accountId
1171
+ });
1172
+ return await response.text();
1173
+ }
1174
+ } catch (error) {
1175
+ if (error.message && error.message.includes("key not found")) {
1176
+ return null;
1177
+ }
1178
+ const message = error instanceof Error ? error.message : String(error);
1179
+ this.logger.error(`Failed to get value for ${tableName} ${key}:`, { message });
1180
+ throw error;
1181
+ }
1182
+ }
1183
+ async getKV(tableName, key) {
1184
+ try {
1185
+ const text = await this.getNamespaceValue(tableName, key);
1186
+ return this.safeParse(text);
1187
+ } catch (error) {
1188
+ this.logger.error(`Failed to get KV value for ${tableName}:${key}:`, error);
1189
+ throw new Error(`Failed to get KV value: ${error.message}`);
1190
+ }
1191
+ }
456
1192
  async getTableSchema(tableName) {
457
1193
  try {
458
1194
  const schemaKey = this.getSchemaKey(tableName);
@@ -505,64 +1241,168 @@ var CloudflareStore = class extends MastraStorage {
505
1241
  }
506
1242
  } catch (error) {
507
1243
  const message = error instanceof Error ? error.message : String(error);
508
- this.logger.error(`Error validating record against schema:`, { message, record, schema });
1244
+ this.logger.error(`Error validating record against schema:`, { message, record, schema });
1245
+ throw error;
1246
+ }
1247
+ }
1248
+ async validateRecord(record, tableName) {
1249
+ try {
1250
+ if (!record || typeof record !== "object") {
1251
+ throw new Error("Record must be an object");
1252
+ }
1253
+ const recordTyped = record;
1254
+ const schema = await this.getTableSchema(tableName);
1255
+ if (schema) {
1256
+ await this.validateAgainstSchema(recordTyped, schema);
1257
+ return;
1258
+ }
1259
+ switch (tableName) {
1260
+ case TABLE_THREADS:
1261
+ if (!("id" in recordTyped) || !("resourceId" in recordTyped) || !("title" in recordTyped)) {
1262
+ throw new Error("Thread record missing required fields");
1263
+ }
1264
+ break;
1265
+ case TABLE_MESSAGES:
1266
+ if (!("id" in recordTyped) || !("threadId" in recordTyped) || !("content" in recordTyped) || !("role" in recordTyped)) {
1267
+ throw new Error("Message record missing required fields");
1268
+ }
1269
+ break;
1270
+ case TABLE_WORKFLOW_SNAPSHOT:
1271
+ if (!("workflow_name" in recordTyped) || !("run_id" in recordTyped)) {
1272
+ throw new Error("Workflow record missing required fields");
1273
+ }
1274
+ break;
1275
+ case TABLE_TRACES:
1276
+ if (!("id" in recordTyped)) {
1277
+ throw new Error("Trace record missing required fields");
1278
+ }
1279
+ break;
1280
+ case TABLE_SCORERS:
1281
+ if (!("id" in recordTyped) || !("scorerId" in recordTyped)) {
1282
+ throw new Error("Score record missing required fields");
1283
+ }
1284
+ break;
1285
+ default:
1286
+ throw new Error(`Unknown table type: ${tableName}`);
1287
+ }
1288
+ } catch (error) {
1289
+ const message = error instanceof Error ? error.message : String(error);
1290
+ this.logger.error(`Failed to validate record for ${tableName}:`, { message, record });
1291
+ throw error;
1292
+ }
1293
+ }
1294
+ async insert({ tableName, record }) {
1295
+ try {
1296
+ const key = this.getKey(tableName, record);
1297
+ const processedRecord = { ...record };
1298
+ await this.validateRecord(processedRecord, tableName);
1299
+ await this.putKV({ tableName, key, value: processedRecord });
1300
+ } catch (error) {
1301
+ throw new MastraError(
1302
+ {
1303
+ id: "CLOUDFLARE_STORAGE_INSERT_FAILED",
1304
+ domain: ErrorDomain.STORAGE,
1305
+ category: ErrorCategory.THIRD_PARTY,
1306
+ details: {
1307
+ tableName
1308
+ }
1309
+ },
1310
+ error
1311
+ );
1312
+ }
1313
+ }
1314
+ async load({ tableName, keys }) {
1315
+ try {
1316
+ const key = this.getKey(tableName, keys);
1317
+ const data = await this.getKV(tableName, key);
1318
+ if (!data) return null;
1319
+ return data;
1320
+ } catch (error) {
1321
+ const mastraError = new MastraError(
1322
+ {
1323
+ id: "CLOUDFLARE_STORAGE_LOAD_FAILED",
1324
+ domain: ErrorDomain.STORAGE,
1325
+ category: ErrorCategory.THIRD_PARTY,
1326
+ details: {
1327
+ tableName
1328
+ }
1329
+ },
1330
+ error
1331
+ );
1332
+ this.logger?.trackException(mastraError);
1333
+ this.logger?.error(mastraError.toString());
1334
+ return null;
1335
+ }
1336
+ }
1337
+ async batchInsert(input) {
1338
+ if (!input.records || input.records.length === 0) return;
1339
+ try {
1340
+ await Promise.all(
1341
+ input.records.map(async (record) => {
1342
+ const key = this.getKey(input.tableName, record);
1343
+ await this.putKV({ tableName: input.tableName, key, value: record });
1344
+ })
1345
+ );
1346
+ } catch (error) {
1347
+ throw new MastraError(
1348
+ {
1349
+ id: "CLOUDFLARE_STORAGE_BATCH_INSERT_FAILED",
1350
+ domain: ErrorDomain.STORAGE,
1351
+ category: ErrorCategory.THIRD_PARTY,
1352
+ text: `Error in batch insert for table ${input.tableName}`,
1353
+ details: {
1354
+ tableName: input.tableName
1355
+ }
1356
+ },
1357
+ error
1358
+ );
1359
+ }
1360
+ }
1361
+ /**
1362
+ * Helper to safely serialize data for KV storage
1363
+ */
1364
+ safeSerialize(data) {
1365
+ return typeof data === "string" ? data : JSON.stringify(data);
1366
+ }
1367
+ async putNamespaceValue({
1368
+ tableName,
1369
+ key,
1370
+ value,
1371
+ metadata
1372
+ }) {
1373
+ try {
1374
+ const serializedValue = this.safeSerialize(value);
1375
+ const serializedMetadata = metadata ? this.safeSerialize(metadata) : "";
1376
+ if (this.bindings) {
1377
+ const binding = this.getBinding(tableName);
1378
+ await binding.put(key, serializedValue, { metadata: serializedMetadata });
1379
+ } else {
1380
+ const namespaceId = await this.getNamespaceId(tableName);
1381
+ await this.client.kv.namespaces.values.update(namespaceId, key, {
1382
+ account_id: this.accountId,
1383
+ value: serializedValue,
1384
+ metadata: serializedMetadata
1385
+ });
1386
+ }
1387
+ } catch (error) {
1388
+ const message = error instanceof Error ? error.message : String(error);
1389
+ this.logger.error(`Failed to put value for ${tableName} ${key}:`, { message });
509
1390
  throw error;
510
1391
  }
511
1392
  }
512
- async validateRecord(record, tableName) {
1393
+ async putKV({
1394
+ tableName,
1395
+ key,
1396
+ value,
1397
+ metadata
1398
+ }) {
513
1399
  try {
514
- if (!record || typeof record !== "object") {
515
- throw new Error("Record must be an object");
516
- }
517
- const recordTyped = record;
518
- const schema = await this.getTableSchema(tableName);
519
- if (schema) {
520
- await this.validateAgainstSchema(recordTyped, schema);
521
- return;
522
- }
523
- switch (tableName) {
524
- case TABLE_THREADS:
525
- if (!("id" in recordTyped) || !("resourceId" in recordTyped) || !("title" in recordTyped)) {
526
- throw new Error("Thread record missing required fields");
527
- }
528
- break;
529
- case TABLE_MESSAGES:
530
- if (!("id" in recordTyped) || !("threadId" in recordTyped) || !("content" in recordTyped) || !("role" in recordTyped)) {
531
- throw new Error("Message record missing required fields");
532
- }
533
- break;
534
- case TABLE_WORKFLOW_SNAPSHOT:
535
- if (!("namespace" in recordTyped) || !("workflow_name" in recordTyped) || !("run_id" in recordTyped)) {
536
- throw new Error("Workflow record missing required fields");
537
- }
538
- break;
539
- case TABLE_TRACES:
540
- if (!("id" in recordTyped)) {
541
- throw new Error("Trace record missing required fields");
542
- }
543
- break;
544
- default:
545
- throw new Error(`Unknown table type: ${tableName}`);
546
- }
1400
+ await this.putNamespaceValue({ tableName, key, value, metadata });
547
1401
  } catch (error) {
548
- const message = error instanceof Error ? error.message : String(error);
549
- this.logger.error(`Failed to validate record for ${tableName}:`, { message, record });
550
- throw error;
1402
+ this.logger.error(`Failed to put KV value for ${tableName}:${key}:`, error);
1403
+ throw new Error(`Failed to put KV value: ${error.message}`);
551
1404
  }
552
1405
  }
553
- ensureDate(date) {
554
- if (!date) return void 0;
555
- return date instanceof Date ? date : new Date(date);
556
- }
557
- serializeDate(date) {
558
- if (!date) return void 0;
559
- const dateObj = this.ensureDate(date);
560
- return dateObj?.toISOString();
561
- }
562
- ensureMetadata(metadata) {
563
- if (!metadata) return {};
564
- return typeof metadata === "string" ? JSON.parse(metadata) : metadata;
565
- }
566
1406
  async createTable({
567
1407
  tableName,
568
1408
  schema
@@ -576,475 +1416,467 @@ var CloudflareStore = class extends MastraStorage {
576
1416
  };
577
1417
  await this.putKV({ tableName, key: schemaKey, value: schema, metadata });
578
1418
  } catch (error) {
579
- this.logger.error(`Failed to store schema for ${tableName}:`, error);
580
- throw new Error(`Failed to store schema: ${error.message}`);
581
- }
582
- }
583
- async clearTable({ tableName }) {
584
- const keys = await this.listKV(tableName);
585
- if (keys.length > 0) {
586
- await Promise.all(keys.map((keyObj) => this.deleteKV(tableName, keyObj.name)));
587
- }
588
- }
589
- async insert({
590
- tableName,
591
- record
592
- }) {
593
- try {
594
- const key = this.getKey(tableName, record);
595
- const processedRecord = {
596
- ...record,
597
- createdAt: record.createdAt ? this.serializeDate(record.createdAt) : void 0,
598
- updatedAt: record.updatedAt ? this.serializeDate(record.updatedAt) : void 0,
599
- metadata: record.metadata ? JSON.stringify(record.metadata) : ""
600
- };
601
- await this.validateRecord(processedRecord, tableName);
602
- await this.putKV({ tableName, key, value: processedRecord });
603
- } catch (error) {
604
- const message = error instanceof Error ? error.message : String(error);
605
- this.logger.error(`Failed to insert record for ${tableName}:`, { message });
606
- throw error;
1419
+ throw new MastraError(
1420
+ {
1421
+ id: "CLOUDFLARE_STORAGE_CREATE_TABLE_FAILED",
1422
+ domain: ErrorDomain.STORAGE,
1423
+ category: ErrorCategory.THIRD_PARTY,
1424
+ details: {
1425
+ tableName
1426
+ }
1427
+ },
1428
+ error
1429
+ );
607
1430
  }
608
1431
  }
609
- async load({ tableName, keys }) {
1432
+ async listNamespaceKeys(tableName, options) {
610
1433
  try {
611
- const key = this.getKey(tableName, keys);
612
- const data = await this.getKV(tableName, key);
613
- if (!data) return null;
614
- const processed = {
615
- ...data,
616
- createdAt: this.ensureDate(data.createdAt),
617
- updatedAt: this.ensureDate(data.updatedAt),
618
- metadata: this.ensureMetadata(data.metadata)
619
- };
620
- return processed;
1434
+ if (this.bindings) {
1435
+ const binding = this.getBinding(tableName);
1436
+ const response = await binding.list({
1437
+ limit: options?.limit || 1e3,
1438
+ prefix: options?.prefix
1439
+ });
1440
+ return response.keys;
1441
+ } else {
1442
+ const namespaceId = await this.getNamespaceId(tableName);
1443
+ const response = await this.client.kv.namespaces.keys.list(namespaceId, {
1444
+ account_id: this.accountId,
1445
+ limit: options?.limit || 1e3,
1446
+ prefix: options?.prefix
1447
+ });
1448
+ return response.result;
1449
+ }
621
1450
  } catch (error) {
622
- this.logger.error(`Failed to load data for ${tableName}:`, {
623
- error: error instanceof Error ? error.message : String(error)
624
- });
625
- return null;
1451
+ throw new MastraError(
1452
+ {
1453
+ id: "CLOUDFLARE_STORAGE_LIST_NAMESPACE_KEYS_FAILED",
1454
+ domain: ErrorDomain.STORAGE,
1455
+ category: ErrorCategory.THIRD_PARTY,
1456
+ details: {
1457
+ tableName
1458
+ }
1459
+ },
1460
+ error
1461
+ );
626
1462
  }
627
1463
  }
628
- async getThreadById({ threadId }) {
629
- const thread = await this.load({ tableName: TABLE_THREADS, keys: { id: threadId } });
630
- if (!thread) return null;
631
- try {
632
- return {
633
- ...thread,
634
- createdAt: this.ensureDate(thread.createdAt),
635
- updatedAt: this.ensureDate(thread.updatedAt),
636
- metadata: this.ensureMetadata(thread.metadata)
637
- };
638
- } catch (error) {
639
- this.logger.error(`Error processing thread ${threadId}:`, {
640
- error: error instanceof Error ? error.message : String(error)
1464
+ async deleteNamespaceValue(tableName, key) {
1465
+ if (this.bindings) {
1466
+ const binding = this.getBinding(tableName);
1467
+ await binding.delete(key);
1468
+ } else {
1469
+ const namespaceId = await this.getNamespaceId(tableName);
1470
+ await this.client.kv.namespaces.values.delete(namespaceId, key, {
1471
+ account_id: this.accountId
641
1472
  });
642
- return null;
643
1473
  }
644
1474
  }
645
- async getThreadsByResourceId({ resourceId }) {
1475
+ async deleteKV(tableName, key) {
646
1476
  try {
647
- const keyList = await this.listKV(TABLE_THREADS);
648
- const threads = await Promise.all(
649
- keyList.map(async (keyObj) => {
650
- try {
651
- const data = await this.getKV(TABLE_THREADS, keyObj.name);
652
- if (!data) return null;
653
- const thread = typeof data === "string" ? JSON.parse(data) : data;
654
- if (!thread || !thread.resourceId || thread.resourceId !== resourceId) return null;
655
- return {
656
- ...thread,
657
- createdAt: this.ensureDate(thread.createdAt),
658
- updatedAt: this.ensureDate(thread.updatedAt),
659
- metadata: this.ensureMetadata(thread.metadata)
660
- };
661
- } catch (error) {
662
- const message = error instanceof Error ? error.message : String(error);
663
- this.logger.error(`Error processing thread from key ${keyObj.name}:`, { message });
664
- return null;
665
- }
666
- })
667
- );
668
- return threads.filter((thread) => thread !== null);
1477
+ await this.deleteNamespaceValue(tableName, key);
669
1478
  } catch (error) {
670
- const message = error instanceof Error ? error.message : String(error);
671
- this.logger.error(`Error getting threads for resourceId ${resourceId}:`, { message });
672
- return [];
1479
+ this.logger.error(`Failed to delete KV value for ${tableName}:${key}:`, error);
1480
+ throw new Error(`Failed to delete KV value: ${error.message}`);
673
1481
  }
674
1482
  }
675
- async saveThread({ thread }) {
1483
+ async listKV(tableName, options) {
676
1484
  try {
677
- await this.insert({ tableName: TABLE_THREADS, record: thread });
678
- return thread;
1485
+ return await this.listNamespaceKeys(tableName, options);
679
1486
  } catch (error) {
680
- const message = error instanceof Error ? error.message : String(error);
681
- this.logger.error("Error saving thread:", { message });
682
- throw error;
1487
+ this.logger.error(`Failed to list KV for ${tableName}:`, error);
1488
+ throw new Error(`Failed to list KV: ${error.message}`);
683
1489
  }
684
1490
  }
685
- async updateThread({
686
- id,
687
- title,
688
- metadata
689
- }) {
690
- try {
691
- const thread = await this.getThreadById({ threadId: id });
692
- if (!thread) {
693
- throw new Error(`Thread ${id} not found`);
694
- }
695
- const updatedThread = {
696
- ...thread,
697
- title,
698
- metadata: this.ensureMetadata({
699
- ...thread.metadata ?? {},
700
- ...metadata
701
- }),
702
- updatedAt: /* @__PURE__ */ new Date()
703
- };
704
- await this.insert({ tableName: TABLE_THREADS, record: updatedThread });
705
- return updatedThread;
706
- } catch (error) {
707
- const message = error instanceof Error ? error.message : String(error);
708
- this.logger.error(`Error updating thread ${id}:`, { message });
709
- throw error;
710
- }
1491
+ };
1492
+ function transformScoreRow(row) {
1493
+ const deserialized = { ...row };
1494
+ deserialized.input = safelyParseJSON(row.input);
1495
+ deserialized.output = safelyParseJSON(row.output);
1496
+ deserialized.scorer = safelyParseJSON(row.scorer);
1497
+ deserialized.preprocessStepResult = safelyParseJSON(row.preprocessStepResult);
1498
+ deserialized.analyzeStepResult = safelyParseJSON(row.analyzeStepResult);
1499
+ deserialized.metadata = safelyParseJSON(row.metadata);
1500
+ deserialized.additionalContext = safelyParseJSON(row.additionalContext);
1501
+ deserialized.requestContext = safelyParseJSON(row.requestContext);
1502
+ deserialized.entity = safelyParseJSON(row.entity);
1503
+ return deserialized;
1504
+ }
1505
+ var ScoresStorageCloudflare = class extends ScoresStorage {
1506
+ operations;
1507
+ constructor({ operations }) {
1508
+ super();
1509
+ this.operations = operations;
711
1510
  }
712
- async deleteThread({ threadId }) {
1511
+ async getScoreById({ id }) {
713
1512
  try {
714
- const thread = await this.getThreadById({ threadId });
715
- if (!thread) {
716
- throw new Error(`Thread ${threadId} not found`);
1513
+ const score = await this.operations.getKV(TABLE_SCORERS, id);
1514
+ if (!score) {
1515
+ return null;
717
1516
  }
718
- const messageKeys = await this.listKV(TABLE_MESSAGES);
719
- const threadMessageKeys = messageKeys.filter((key) => key.name.includes(`${TABLE_MESSAGES}:${threadId}:`));
720
- await Promise.all([
721
- // Delete message order
722
- this.deleteKV(TABLE_MESSAGES, this.getThreadMessagesKey(threadId)),
723
- // Delete all messages
724
- ...threadMessageKeys.map((key) => this.deleteKV(TABLE_MESSAGES, key.name)),
725
- // Delete thread
726
- this.deleteKV(TABLE_THREADS, this.getKey(TABLE_THREADS, { id: threadId }))
727
- ]);
1517
+ return transformScoreRow(score);
728
1518
  } catch (error) {
729
- const message = error instanceof Error ? error.message : String(error);
730
- this.logger.error(`Error deleting thread ${threadId}:`, { message });
731
- throw error;
1519
+ const mastraError = new MastraError(
1520
+ {
1521
+ id: "CLOUDFLARE_STORAGE_SCORES_GET_SCORE_BY_ID_FAILED",
1522
+ domain: ErrorDomain.STORAGE,
1523
+ category: ErrorCategory.THIRD_PARTY,
1524
+ text: `Failed to get score by id: ${id}`
1525
+ },
1526
+ error
1527
+ );
1528
+ this.logger.trackException(mastraError);
1529
+ this.logger.error(mastraError.toString());
1530
+ return null;
732
1531
  }
733
1532
  }
734
- getMessageKey(threadId, messageId) {
1533
+ async saveScore(score) {
1534
+ let parsedScore;
735
1535
  try {
736
- return this.getKey(TABLE_MESSAGES, { threadId, id: messageId });
1536
+ parsedScore = saveScorePayloadSchema.parse(score);
737
1537
  } catch (error) {
738
- const message = error instanceof Error ? error.message : String(error);
739
- this.logger.error(`Error getting message key for thread ${threadId} and message ${messageId}:`, { message });
740
- throw error;
1538
+ throw new MastraError(
1539
+ {
1540
+ id: "CLOUDFLARE_STORAGE_SAVE_SCORE_FAILED_INVALID_SCORE_PAYLOAD",
1541
+ domain: ErrorDomain.STORAGE,
1542
+ category: ErrorCategory.USER,
1543
+ details: { scoreId: score.id }
1544
+ },
1545
+ error
1546
+ );
741
1547
  }
742
- }
743
- getThreadMessagesKey(threadId) {
744
1548
  try {
745
- return this.getKey(TABLE_MESSAGES, { threadId, id: "messages" });
1549
+ const id = crypto.randomUUID();
1550
+ const serializedRecord = {};
1551
+ for (const [key, value] of Object.entries(parsedScore)) {
1552
+ if (value !== null && value !== void 0) {
1553
+ if (typeof value === "object") {
1554
+ serializedRecord[key] = JSON.stringify(value);
1555
+ } else {
1556
+ serializedRecord[key] = value;
1557
+ }
1558
+ } else {
1559
+ serializedRecord[key] = null;
1560
+ }
1561
+ }
1562
+ serializedRecord.id = id;
1563
+ serializedRecord.createdAt = (/* @__PURE__ */ new Date()).toISOString();
1564
+ serializedRecord.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1565
+ await this.operations.putKV({
1566
+ tableName: TABLE_SCORERS,
1567
+ key: id,
1568
+ value: serializedRecord
1569
+ });
1570
+ const scoreFromDb = await this.getScoreById({ id: score.id });
1571
+ return { score: scoreFromDb };
746
1572
  } catch (error) {
747
- const message = error instanceof Error ? error.message : String(error);
748
- this.logger.error(`Error getting thread messages key for thread ${threadId}:`, { message });
749
- throw error;
1573
+ const mastraError = new MastraError(
1574
+ {
1575
+ id: "CLOUDFLARE_STORAGE_SCORES_SAVE_SCORE_FAILED",
1576
+ domain: ErrorDomain.STORAGE,
1577
+ category: ErrorCategory.THIRD_PARTY,
1578
+ text: `Failed to save score: ${score.id}`
1579
+ },
1580
+ error
1581
+ );
1582
+ this.logger.trackException(mastraError);
1583
+ this.logger.error(mastraError.toString());
1584
+ throw mastraError;
750
1585
  }
751
1586
  }
752
- async saveMessages({ messages }) {
753
- if (!Array.isArray(messages) || messages.length === 0) return [];
1587
+ async listScoresByScorerId({
1588
+ scorerId,
1589
+ entityId,
1590
+ entityType,
1591
+ source,
1592
+ pagination
1593
+ }) {
754
1594
  try {
755
- const validatedMessages = messages.map((message, index) => {
756
- const errors = [];
757
- if (!message.id) errors.push("id is required");
758
- if (!message.threadId) errors.push("threadId is required");
759
- if (!message.content) errors.push("content is required");
760
- if (!message.role) errors.push("role is required");
761
- if (!message.createdAt) errors.push("createdAt is required");
762
- if (errors.length > 0) {
763
- throw new Error(`Invalid message at index ${index}: ${errors.join(", ")}`);
1595
+ const keys = await this.operations.listKV(TABLE_SCORERS);
1596
+ const scores = [];
1597
+ for (const { name: key } of keys) {
1598
+ const score = await this.operations.getKV(TABLE_SCORERS, key);
1599
+ if (entityId && score.entityId !== entityId) {
1600
+ continue;
764
1601
  }
765
- return {
766
- ...message,
767
- createdAt: this.ensureDate(message.createdAt),
768
- type: message.type || "text",
769
- _index: index
770
- };
771
- });
772
- const messagesByThread = validatedMessages.reduce((acc, message) => {
773
- if (!acc.has(message.threadId)) {
774
- acc.set(message.threadId, []);
1602
+ if (entityType && score.entityType !== entityType) {
1603
+ continue;
775
1604
  }
776
- acc.get(message.threadId).push(message);
777
- return acc;
778
- }, /* @__PURE__ */ new Map());
779
- await Promise.all(
780
- Array.from(messagesByThread.entries()).map(async ([threadId, threadMessages]) => {
781
- try {
782
- const thread = await this.getThreadById({ threadId });
783
- if (!thread) {
784
- throw new Error(`Thread ${threadId} not found`);
785
- }
786
- await Promise.all(
787
- threadMessages.map(async (message) => {
788
- const key = await this.getMessageKey(threadId, message.id);
789
- const { _index, ...cleanMessage } = message;
790
- const serializedMessage = {
791
- ...cleanMessage,
792
- createdAt: this.serializeDate(cleanMessage.createdAt)
793
- };
794
- await this.putKV({ tableName: TABLE_MESSAGES, key, value: serializedMessage });
795
- })
796
- );
797
- const orderKey = this.getThreadMessagesKey(threadId);
798
- const entries = await this.updateSorting(threadMessages);
799
- await this.updateSortedMessages(orderKey, entries);
800
- } catch (error) {
801
- const errorMessage = error instanceof Error ? error.message : String(error);
802
- this.logger.error(`Error processing messages for thread ${threadId}: ${errorMessage}`);
803
- throw error;
804
- }
805
- })
1605
+ if (source && score.source !== source) {
1606
+ continue;
1607
+ }
1608
+ if (score && score.scorerId === scorerId) {
1609
+ scores.push(transformScoreRow(score));
1610
+ }
1611
+ }
1612
+ scores.sort((a, b) => {
1613
+ const dateA = new Date(a.createdAt || 0).getTime();
1614
+ const dateB = new Date(b.createdAt || 0).getTime();
1615
+ return dateB - dateA;
1616
+ });
1617
+ const { page, perPage: perPageInput } = pagination;
1618
+ const perPage = normalizePerPage(perPageInput, 100);
1619
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1620
+ const total = scores.length;
1621
+ const end = perPageInput === false ? scores.length : start + perPage;
1622
+ const pagedScores = scores.slice(start, end);
1623
+ return {
1624
+ pagination: {
1625
+ total,
1626
+ page,
1627
+ perPage: perPageForResponse,
1628
+ hasMore: end < total
1629
+ },
1630
+ scores: pagedScores
1631
+ };
1632
+ } catch (error) {
1633
+ const mastraError = new MastraError(
1634
+ {
1635
+ id: "CLOUDFLARE_STORAGE_SCORES_GET_SCORES_BY_SCORER_ID_FAILED",
1636
+ domain: ErrorDomain.STORAGE,
1637
+ category: ErrorCategory.THIRD_PARTY,
1638
+ text: `Failed to get scores by scorer id: ${scorerId}`
1639
+ },
1640
+ error
806
1641
  );
807
- return validatedMessages.map(({ _index, ...message }) => message);
1642
+ this.logger?.trackException(mastraError);
1643
+ this.logger?.error(mastraError.toString());
1644
+ return { pagination: { total: 0, page: 0, perPage: 100, hasMore: false }, scores: [] };
1645
+ }
1646
+ }
1647
+ async listScoresByRunId({
1648
+ runId,
1649
+ pagination
1650
+ }) {
1651
+ try {
1652
+ const keys = await this.operations.listKV(TABLE_SCORERS);
1653
+ const scores = [];
1654
+ for (const { name: key } of keys) {
1655
+ const score = await this.operations.getKV(TABLE_SCORERS, key);
1656
+ if (score && score.runId === runId) {
1657
+ scores.push(transformScoreRow(score));
1658
+ }
1659
+ }
1660
+ scores.sort((a, b) => {
1661
+ const dateA = new Date(a.createdAt || 0).getTime();
1662
+ const dateB = new Date(b.createdAt || 0).getTime();
1663
+ return dateB - dateA;
1664
+ });
1665
+ const { page, perPage: perPageInput } = pagination;
1666
+ const perPage = normalizePerPage(perPageInput, 100);
1667
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1668
+ const total = scores.length;
1669
+ const end = perPageInput === false ? scores.length : start + perPage;
1670
+ const pagedScores = scores.slice(start, end);
1671
+ return {
1672
+ pagination: {
1673
+ total,
1674
+ page,
1675
+ perPage: perPageForResponse,
1676
+ hasMore: end < total
1677
+ },
1678
+ scores: pagedScores
1679
+ };
808
1680
  } catch (error) {
809
- const errorMessage = error instanceof Error ? error.message : String(error);
810
- this.logger.error(`Error saving messages: ${errorMessage}`);
811
- throw error;
1681
+ const mastraError = new MastraError(
1682
+ {
1683
+ id: "CLOUDFLARE_STORAGE_SCORES_GET_SCORES_BY_RUN_ID_FAILED",
1684
+ domain: ErrorDomain.STORAGE,
1685
+ category: ErrorCategory.THIRD_PARTY,
1686
+ text: `Failed to get scores by run id: ${runId}`
1687
+ },
1688
+ error
1689
+ );
1690
+ this.logger.trackException(mastraError);
1691
+ this.logger.error(mastraError.toString());
1692
+ return { pagination: { total: 0, page: 0, perPage: 100, hasMore: false }, scores: [] };
812
1693
  }
813
1694
  }
814
- async getMessages({ threadId, selectBy }) {
815
- if (!threadId) throw new Error("threadId is required");
816
- let limit = 40;
817
- if (typeof selectBy?.last === "number") {
818
- limit = Math.max(0, selectBy.last);
819
- } else if (selectBy?.last === false) {
820
- limit = 0;
1695
+ async listScoresByEntityId({
1696
+ entityId,
1697
+ entityType,
1698
+ pagination
1699
+ }) {
1700
+ try {
1701
+ const keys = await this.operations.listKV(TABLE_SCORERS);
1702
+ const scores = [];
1703
+ for (const { name: key } of keys) {
1704
+ const score = await this.operations.getKV(TABLE_SCORERS, key);
1705
+ if (score && score.entityId === entityId && score.entityType === entityType) {
1706
+ scores.push(transformScoreRow(score));
1707
+ }
1708
+ }
1709
+ scores.sort((a, b) => {
1710
+ const dateA = new Date(a.createdAt || 0).getTime();
1711
+ const dateB = new Date(b.createdAt || 0).getTime();
1712
+ return dateB - dateA;
1713
+ });
1714
+ const { page, perPage: perPageInput } = pagination;
1715
+ const perPage = normalizePerPage(perPageInput, 100);
1716
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1717
+ const total = scores.length;
1718
+ const end = perPageInput === false ? scores.length : start + perPage;
1719
+ const pagedScores = scores.slice(start, end);
1720
+ return {
1721
+ pagination: {
1722
+ total,
1723
+ page,
1724
+ perPage: perPageForResponse,
1725
+ hasMore: end < total
1726
+ },
1727
+ scores: pagedScores
1728
+ };
1729
+ } catch (error) {
1730
+ const mastraError = new MastraError(
1731
+ {
1732
+ id: "CLOUDFLARE_STORAGE_SCORES_GET_SCORES_BY_ENTITY_ID_FAILED",
1733
+ domain: ErrorDomain.STORAGE,
1734
+ category: ErrorCategory.THIRD_PARTY,
1735
+ text: `Failed to get scores by entity id: ${entityId}, type: ${entityType}`
1736
+ },
1737
+ error
1738
+ );
1739
+ this.logger.trackException(mastraError);
1740
+ this.logger.error(mastraError.toString());
1741
+ return { pagination: { total: 0, page: 0, perPage: 100, hasMore: false }, scores: [] };
821
1742
  }
822
- const messageIds = /* @__PURE__ */ new Set();
823
- if (limit === 0 && !selectBy?.include?.length) return [];
1743
+ }
1744
+ async listScoresBySpan({
1745
+ traceId,
1746
+ spanId,
1747
+ pagination
1748
+ }) {
824
1749
  try {
825
- await Promise.all([
826
- selectBy?.include?.length ? this.getIncludedMessagesWithContext(threadId, selectBy.include, messageIds) : Promise.resolve(),
827
- limit > 0 && !selectBy?.include?.length ? this.getRecentMessages(threadId, limit, messageIds) : Promise.resolve()
828
- ]);
829
- const messages = await this.fetchAndParseMessages(threadId, Array.from(messageIds));
830
- if (!messages.length) return [];
831
- try {
832
- const threadMessagesKey = this.getThreadMessagesKey(threadId);
833
- const messageOrder = await this.getFullOrder(threadMessagesKey);
834
- const orderMap = new Map(messageOrder.map((id, index) => [id, index]));
835
- messages.sort((a, b) => {
836
- const indexA = orderMap.get(a.id);
837
- const indexB = orderMap.get(b.id);
838
- if (indexA !== void 0 && indexB !== void 0) return orderMap.get(a.id) - orderMap.get(b.id);
839
- return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
840
- });
841
- } catch (error) {
842
- const errorMessage = error instanceof Error ? error.message : String(error);
843
- this.logger.warn(`Error sorting messages, falling back to creation time: ${errorMessage}`);
844
- messages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
1750
+ const keys = await this.operations.listKV(TABLE_SCORERS);
1751
+ const scores = [];
1752
+ for (const { name: key } of keys) {
1753
+ const score = await this.operations.getKV(TABLE_SCORERS, key);
1754
+ if (score && score.traceId === traceId && score.spanId === spanId) {
1755
+ scores.push(transformScoreRow(score));
1756
+ }
845
1757
  }
846
- return messages.map(({ _index, ...message }) => ({
847
- ...message,
848
- createdAt: this.ensureDate(message.createdAt)
849
- }));
1758
+ scores.sort((a, b) => {
1759
+ const dateA = new Date(a.createdAt || 0).getTime();
1760
+ const dateB = new Date(b.createdAt || 0).getTime();
1761
+ return dateB - dateA;
1762
+ });
1763
+ const { page, perPage: perPageInput } = pagination;
1764
+ const perPage = normalizePerPage(perPageInput, 100);
1765
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1766
+ const total = scores.length;
1767
+ const end = perPageInput === false ? scores.length : start + perPage;
1768
+ const pagedScores = scores.slice(start, end);
1769
+ return {
1770
+ pagination: {
1771
+ total,
1772
+ page,
1773
+ perPage: perPageForResponse,
1774
+ hasMore: end < total
1775
+ },
1776
+ scores: pagedScores
1777
+ };
850
1778
  } catch (error) {
851
- const errorMessage = error instanceof Error ? error.message : String(error);
852
- this.logger.error(`Error retrieving messages for thread ${threadId}: ${errorMessage}`);
853
- return [];
1779
+ const mastraError = new MastraError(
1780
+ {
1781
+ id: "CLOUDFLARE_STORAGE_SCORES_GET_SCORES_BY_SPAN_FAILED",
1782
+ domain: ErrorDomain.STORAGE,
1783
+ category: ErrorCategory.THIRD_PARTY,
1784
+ text: `Failed to get scores by span: traceId=${traceId}, spanId=${spanId}`
1785
+ },
1786
+ error
1787
+ );
1788
+ this.logger?.trackException(mastraError);
1789
+ this.logger?.error(mastraError.toString());
1790
+ return { pagination: { total: 0, page: 0, perPage: 100, hasMore: false }, scores: [] };
854
1791
  }
855
1792
  }
1793
+ };
1794
+ var WorkflowsStorageCloudflare = class extends WorkflowsStorage {
1795
+ operations;
1796
+ constructor({ operations }) {
1797
+ super();
1798
+ this.operations = operations;
1799
+ }
856
1800
  validateWorkflowParams(params) {
857
- const { namespace, workflowName, runId } = params;
858
- if (!namespace || !workflowName || !runId) {
1801
+ const { workflowName, runId } = params;
1802
+ if (!workflowName || !runId) {
859
1803
  throw new Error("Invalid workflow snapshot parameters");
860
1804
  }
861
1805
  }
862
- validateWorkflowState(state) {
863
- if (!state?.runId || !state?.value || !state?.context?.steps || !state?.context?.triggerData || !state?.context?.attempts || !state?.activePaths) {
864
- throw new Error("Invalid workflow state structure");
865
- }
866
- }
867
- normalizeSteps(steps) {
868
- const normalizedSteps = {};
869
- for (const [stepId, step] of Object.entries(steps)) {
870
- normalizedSteps[stepId] = {
871
- status: step.status,
872
- payload: step.payload || step.result,
873
- error: step.error
874
- };
875
- }
876
- return normalizedSteps;
1806
+ updateWorkflowResults({
1807
+ // workflowName,
1808
+ // runId,
1809
+ // stepId,
1810
+ // result,
1811
+ // requestContext,
1812
+ }) {
1813
+ throw new Error("Method not implemented.");
877
1814
  }
878
- normalizeWorkflowState(data) {
879
- const steps = data.context?.stepResults || data.context?.steps || {};
880
- return {
881
- runId: data.runId,
882
- value: data.value,
883
- context: {
884
- steps: this.normalizeSteps(steps),
885
- triggerData: data.context?.triggerData || {},
886
- attempts: data.context?.attempts || {}
887
- },
888
- suspendedPaths: data.suspendedPaths || {},
889
- activePaths: data.activePaths || [],
890
- timestamp: data.timestamp || Date.now()
891
- };
1815
+ updateWorkflowState({
1816
+ // workflowName,
1817
+ // runId,
1818
+ // opts,
1819
+ }) {
1820
+ throw new Error("Method not implemented.");
892
1821
  }
893
1822
  async persistWorkflowSnapshot(params) {
894
1823
  try {
895
- this.validateWorkflowParams(params);
896
- const { namespace, workflowName, runId, snapshot } = params;
897
- const normalizedState = this.normalizeWorkflowState(snapshot);
898
- this.validateWorkflowState(normalizedState);
899
- await this.insert({
1824
+ const { workflowName, runId, resourceId, snapshot } = params;
1825
+ await this.operations.putKV({
900
1826
  tableName: TABLE_WORKFLOW_SNAPSHOT,
901
- record: {
902
- namespace,
1827
+ key: this.operations.getKey(TABLE_WORKFLOW_SNAPSHOT, { workflow_name: workflowName, run_id: runId }),
1828
+ value: {
903
1829
  workflow_name: workflowName,
904
1830
  run_id: runId,
905
- snapshot: normalizedState,
1831
+ resourceId,
1832
+ snapshot: JSON.stringify(snapshot),
906
1833
  createdAt: /* @__PURE__ */ new Date(),
907
1834
  updatedAt: /* @__PURE__ */ new Date()
908
1835
  }
909
1836
  });
910
1837
  } catch (error) {
911
- const message = error instanceof Error ? error.message : String(error);
912
- this.logger.error("Error persisting workflow snapshot:", { message });
913
- throw error;
1838
+ throw new MastraError(
1839
+ {
1840
+ id: "CLOUDFLARE_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_FAILED",
1841
+ domain: ErrorDomain.STORAGE,
1842
+ category: ErrorCategory.THIRD_PARTY,
1843
+ text: `Error persisting workflow snapshot for workflow ${params.workflowName}, run ${params.runId}`,
1844
+ details: {
1845
+ workflowName: params.workflowName,
1846
+ runId: params.runId
1847
+ }
1848
+ },
1849
+ error
1850
+ );
914
1851
  }
915
1852
  }
916
1853
  async loadWorkflowSnapshot(params) {
917
1854
  try {
918
1855
  this.validateWorkflowParams(params);
919
- const { namespace, workflowName, runId } = params;
920
- const key = this.getKey(TABLE_WORKFLOW_SNAPSHOT, { namespace, workflow_name: workflowName, run_id: runId });
921
- const data = await this.getKV(TABLE_WORKFLOW_SNAPSHOT, key);
1856
+ const { workflowName, runId } = params;
1857
+ const key = this.operations.getKey(TABLE_WORKFLOW_SNAPSHOT, { workflow_name: workflowName, run_id: runId });
1858
+ const data = await this.operations.getKV(TABLE_WORKFLOW_SNAPSHOT, key);
922
1859
  if (!data) return null;
923
- const state = this.normalizeWorkflowState(data.snapshot || data);
924
- this.validateWorkflowState(state);
925
- return state;
926
- } catch (error) {
927
- this.logger.error("Error loading workflow snapshot:", {
928
- error: error instanceof Error ? error.message : String(error)
929
- });
930
- return null;
931
- }
932
- }
933
- async batchInsert(input) {
934
- if (!input.records || input.records.length === 0) return;
935
- try {
936
- await Promise.all(
937
- input.records.map(async (record) => {
938
- const key = this.getKey(input.tableName, record);
939
- const processedRecord = {
940
- ...record,
941
- createdAt: record.createdAt ? this.serializeDate(record.createdAt) : void 0,
942
- updatedAt: record.updatedAt ? this.serializeDate(record.updatedAt) : void 0,
943
- metadata: record.metadata ? JSON.stringify(record.metadata) : void 0
944
- };
945
- await this.putKV({ tableName: input.tableName, key, value: processedRecord });
946
- })
947
- );
1860
+ const snapshotData = typeof data.snapshot === "string" ? JSON.parse(data.snapshot) : data.snapshot;
1861
+ return snapshotData;
948
1862
  } catch (error) {
949
- const message = error instanceof Error ? error.message : String(error);
950
- this.logger.error("Error in batch insert:", { message });
951
- throw error;
952
- }
953
- }
954
- async getTraces({
955
- name,
956
- scope,
957
- page = 0,
958
- perPage = 100,
959
- attributes,
960
- fromDate,
961
- toDate
962
- }) {
963
- try {
964
- let keys;
965
- if (this.bindings) {
966
- keys = (await this.listKV(TABLE_TRACES))?.map((k) => k.name) || [];
967
- } else {
968
- const namespaceId = await this.getNamespaceId(TABLE_TRACES);
969
- const result = await this.client.kv.namespaces.keys.list(namespaceId, {
970
- prefix: "",
971
- limit: 1e3,
972
- account_id: this.accountId
973
- });
974
- keys = result.result?.map((k) => k.name) || [];
975
- }
976
- const traceRecords = await Promise.all(
977
- keys.map(async (key) => {
978
- const record = await this.getKV(TABLE_TRACES, key);
979
- if (!record) return null;
980
- return record;
981
- })
982
- );
983
- let filteredTraces = traceRecords.filter(
984
- (record) => record !== null && typeof record === "object"
1863
+ const mastraError = new MastraError(
1864
+ {
1865
+ id: "CLOUDFLARE_STORAGE_LOAD_WORKFLOW_SNAPSHOT_FAILED",
1866
+ domain: ErrorDomain.STORAGE,
1867
+ category: ErrorCategory.THIRD_PARTY,
1868
+ text: `Error loading workflow snapshot for workflow ${params.workflowName}, run ${params.runId}`,
1869
+ details: {
1870
+ workflowName: params.workflowName,
1871
+ runId: params.runId
1872
+ }
1873
+ },
1874
+ error
985
1875
  );
986
- if (name) {
987
- filteredTraces = filteredTraces.filter((record) => record.name?.toLowerCase().startsWith(name.toLowerCase()));
988
- }
989
- if (scope) {
990
- filteredTraces = filteredTraces.filter((record) => record.scope === scope);
991
- }
992
- if (attributes) {
993
- filteredTraces = filteredTraces.filter((record) => {
994
- if (!record.attributes) return false;
995
- const recordAttrs = this.parseJSON(record.attributes);
996
- if (!recordAttrs) return false;
997
- return Object.entries(attributes).every(([key, value]) => recordAttrs[key] === value);
998
- });
999
- }
1000
- if (fromDate) {
1001
- filteredTraces = filteredTraces.filter((record) => new Date(record.createdAt).getTime() >= fromDate.getTime());
1002
- }
1003
- if (toDate) {
1004
- filteredTraces = filteredTraces.filter((record) => new Date(record.createdAt).getTime() <= toDate.getTime());
1005
- }
1006
- filteredTraces.sort((a, b) => {
1007
- const dateA = new Date(a.createdAt).getTime();
1008
- const dateB = new Date(b.createdAt).getTime();
1009
- return dateB - dateA;
1010
- });
1011
- const start = page * perPage;
1012
- const end = start + perPage;
1013
- const paginatedTraces = filteredTraces.slice(start, end);
1014
- return paginatedTraces.map((record) => ({
1015
- id: record.id,
1016
- parentSpanId: record.parentSpanId,
1017
- traceId: record.traceId,
1018
- name: record.name,
1019
- scope: record.scope,
1020
- kind: record.kind,
1021
- status: this.parseJSON(record.status),
1022
- events: this.parseJSON(record.events) || [],
1023
- links: this.parseJSON(record.links) || [],
1024
- attributes: this.parseJSON(record?.attributes) || {},
1025
- startTime: record.startTime,
1026
- endTime: record.endTime,
1027
- other: this.parseJSON(record.other) || {},
1028
- createdAt: record.createdAt
1029
- }));
1030
- } catch (error) {
1031
- const message = error instanceof Error ? error.message : String(error);
1032
- this.logger.error("Failed to get traces:", { message });
1033
- return [];
1034
- }
1035
- }
1036
- parseJSON(value) {
1037
- if (typeof value === "string") {
1038
- try {
1039
- return JSON.parse(value);
1040
- } catch {
1041
- return value;
1042
- }
1876
+ this.logger.trackException?.(mastraError);
1877
+ this.logger.error(mastraError.toString());
1878
+ return null;
1043
1879
  }
1044
- return value;
1045
- }
1046
- getEvalsByAgentName(_agentName, _type) {
1047
- throw new Error("Method not implemented.");
1048
1880
  }
1049
1881
  parseWorkflowRun(row) {
1050
1882
  let parsedSnapshot = row.snapshot;
@@ -1059,58 +1891,73 @@ var CloudflareStore = class extends MastraStorage {
1059
1891
  workflowName: row.workflow_name,
1060
1892
  runId: row.run_id,
1061
1893
  snapshot: parsedSnapshot,
1062
- createdAt: this.ensureDate(row.createdAt),
1063
- updatedAt: this.ensureDate(row.updatedAt),
1894
+ createdAt: ensureDate(row.createdAt),
1895
+ updatedAt: ensureDate(row.updatedAt),
1064
1896
  resourceId: row.resourceId
1065
1897
  };
1066
1898
  }
1067
1899
  buildWorkflowSnapshotPrefix({
1068
- namespace,
1069
1900
  workflowName,
1070
1901
  runId,
1071
1902
  resourceId
1072
1903
  }) {
1073
- const prefix = this.namespacePrefix ? `${this.namespacePrefix}:` : "";
1904
+ const prefix = this.operations.namespacePrefix ? `${this.operations.namespacePrefix}:` : "";
1074
1905
  let key = `${prefix}${TABLE_WORKFLOW_SNAPSHOT}`;
1075
- if (namespace) key += `:${namespace}`;
1076
1906
  if (workflowName) key += `:${workflowName}`;
1077
1907
  if (runId) key += `:${runId}`;
1078
1908
  if (resourceId) key += `:${resourceId}`;
1079
- if (!resourceId && (runId || workflowName || namespace)) key += ":";
1080
1909
  return key;
1081
1910
  }
1082
- async getWorkflowRuns({
1083
- namespace,
1911
+ async listWorkflowRuns({
1084
1912
  workflowName,
1085
- limit = 20,
1086
- offset = 0,
1913
+ page = 0,
1914
+ perPage = 20,
1087
1915
  resourceId,
1088
1916
  fromDate,
1089
- toDate
1917
+ toDate,
1918
+ status
1090
1919
  } = {}) {
1091
1920
  try {
1092
- const prefix = this.buildWorkflowSnapshotPrefix({ namespace, workflowName });
1093
- const keyObjs = await this.listKV(TABLE_WORKFLOW_SNAPSHOT, { prefix });
1921
+ if (page < 0 || !Number.isInteger(page)) {
1922
+ throw new MastraError(
1923
+ {
1924
+ id: "CLOUDFLARE_STORE_INVALID_PAGE",
1925
+ domain: ErrorDomain.STORAGE,
1926
+ category: ErrorCategory.USER,
1927
+ details: { page }
1928
+ },
1929
+ new Error("page must be a non-negative integer")
1930
+ );
1931
+ }
1932
+ const normalizedPerPage = normalizePerPage(perPage, 20);
1933
+ const offset = page * normalizedPerPage;
1934
+ const prefix = this.buildWorkflowSnapshotPrefix({ workflowName });
1935
+ const keyObjs = await this.operations.listKV(TABLE_WORKFLOW_SNAPSHOT, { prefix });
1094
1936
  const runs = [];
1095
1937
  for (const { name: key } of keyObjs) {
1096
1938
  const parts = key.split(":");
1097
1939
  const idx = parts.indexOf(TABLE_WORKFLOW_SNAPSHOT);
1098
- if (idx === -1 || parts.length < idx + 4) continue;
1099
- const ns = parts[idx + 1];
1100
- const wfName = parts[idx + 2];
1101
- const keyResourceId = parts.length > idx + 4 ? parts[idx + 4] : void 0;
1102
- if (namespace && ns !== namespace || workflowName && wfName !== workflowName) continue;
1103
- if (resourceId && keyResourceId && keyResourceId !== resourceId) continue;
1104
- const data = await this.getKV(TABLE_WORKFLOW_SNAPSHOT, key);
1940
+ if (idx === -1 || parts.length < idx + 3) continue;
1941
+ const wfName = parts[idx + 1];
1942
+ const keyResourceId = parts.length > idx + 3 ? parts[idx + 3] : void 0;
1943
+ if (workflowName && wfName !== workflowName) continue;
1944
+ if (resourceId && keyResourceId !== resourceId) continue;
1945
+ const data = await this.operations.getKV(TABLE_WORKFLOW_SNAPSHOT, key);
1105
1946
  if (!data) continue;
1106
1947
  try {
1107
- if (resourceId && data.resourceId && data.resourceId !== resourceId) continue;
1108
- const createdAt = this.ensureDate(data.createdAt);
1948
+ if (resourceId && !keyResourceId) continue;
1949
+ const snapshotData = typeof data.snapshot === "string" ? JSON.parse(data.snapshot) : data.snapshot;
1950
+ if (status && snapshotData.status !== status) continue;
1951
+ const createdAt = ensureDate(data.createdAt);
1109
1952
  if (fromDate && createdAt && createdAt < fromDate) continue;
1110
1953
  if (toDate && createdAt && createdAt > toDate) continue;
1111
- const state = this.normalizeWorkflowState(data.snapshot || data);
1112
- this.validateWorkflowState(state);
1113
- const run = this.parseWorkflowRun({ ...data, snapshot: state });
1954
+ const resourceIdToUse = keyResourceId || data.resourceId;
1955
+ const run = this.parseWorkflowRun({
1956
+ ...data,
1957
+ workflow_name: wfName,
1958
+ resourceId: resourceIdToUse,
1959
+ snapshot: snapshotData
1960
+ });
1114
1961
  runs.push(run);
1115
1962
  } catch (err) {
1116
1963
  this.logger.error("Failed to parse workflow snapshot:", { key, error: err });
@@ -1121,43 +1968,313 @@ var CloudflareStore = class extends MastraStorage {
1121
1968
  const bDate = b.createdAt ? new Date(b.createdAt).getTime() : 0;
1122
1969
  return bDate - aDate;
1123
1970
  });
1124
- const pagedRuns = runs.slice(offset, offset + limit);
1971
+ const pagedRuns = runs.slice(offset, offset + normalizedPerPage);
1125
1972
  return {
1126
1973
  runs: pagedRuns,
1127
1974
  total: runs.length
1128
1975
  };
1129
1976
  } catch (error) {
1130
- const message = error instanceof Error ? error.message : String(error);
1131
- this.logger.error("Error in getWorkflowRuns:", { message });
1977
+ const mastraError = new MastraError(
1978
+ {
1979
+ id: "CLOUDFLARE_STORAGE_LIST_WORKFLOW_RUNS_FAILED",
1980
+ domain: ErrorDomain.STORAGE,
1981
+ category: ErrorCategory.THIRD_PARTY
1982
+ },
1983
+ error
1984
+ );
1985
+ this.logger.trackException?.(mastraError);
1986
+ this.logger.error(mastraError.toString());
1132
1987
  return { runs: [], total: 0 };
1133
1988
  }
1134
1989
  }
1135
1990
  async getWorkflowRunById({
1136
- namespace,
1137
1991
  runId,
1138
1992
  workflowName
1139
1993
  }) {
1140
1994
  try {
1141
- if (!runId || !workflowName || !namespace) {
1142
- throw new Error("runId, workflowName, and namespace are required");
1995
+ if (!runId || !workflowName) {
1996
+ throw new Error("runId, workflowName, are required");
1143
1997
  }
1144
- const prefix = this.buildWorkflowSnapshotPrefix({ namespace, workflowName, runId });
1145
- const keyObjs = await this.listKV(TABLE_WORKFLOW_SNAPSHOT, { prefix });
1998
+ const prefix = this.buildWorkflowSnapshotPrefix({ workflowName, runId });
1999
+ const keyObjs = await this.operations.listKV(TABLE_WORKFLOW_SNAPSHOT, { prefix });
1146
2000
  if (!keyObjs.length) return null;
1147
- const key = keyObjs[0]?.name;
1148
- const data = await this.getKV(TABLE_WORKFLOW_SNAPSHOT, key);
2001
+ const exactKey = keyObjs.find((k) => {
2002
+ const parts = k.name.split(":");
2003
+ const idx = parts.indexOf(TABLE_WORKFLOW_SNAPSHOT);
2004
+ if (idx === -1 || parts.length < idx + 3) return false;
2005
+ const wfName = parts[idx + 1];
2006
+ const rId = parts[idx + 2];
2007
+ return wfName === workflowName && rId === runId;
2008
+ });
2009
+ if (!exactKey) return null;
2010
+ const data = await this.operations.getKV(TABLE_WORKFLOW_SNAPSHOT, exactKey.name);
1149
2011
  if (!data) return null;
1150
- const state = this.normalizeWorkflowState(data.snapshot || data);
1151
- this.validateWorkflowState(state);
1152
- return this.parseWorkflowRun({ ...data, snapshot: state });
2012
+ const snapshotData = typeof data.snapshot === "string" ? JSON.parse(data.snapshot) : data.snapshot;
2013
+ return this.parseWorkflowRun({ ...data, snapshot: snapshotData });
1153
2014
  } catch (error) {
1154
- const message = error instanceof Error ? error.message : String(error);
1155
- this.logger.error("Error in getWorkflowRunById:", { message });
2015
+ const mastraError = new MastraError(
2016
+ {
2017
+ id: "CLOUDFLARE_STORAGE_GET_WORKFLOW_RUN_BY_ID_FAILED",
2018
+ domain: ErrorDomain.STORAGE,
2019
+ category: ErrorCategory.THIRD_PARTY,
2020
+ details: {
2021
+ workflowName,
2022
+ runId
2023
+ }
2024
+ },
2025
+ error
2026
+ );
2027
+ this.logger.trackException?.(mastraError);
2028
+ this.logger.error(mastraError.toString());
1156
2029
  return null;
1157
2030
  }
1158
2031
  }
2032
+ };
2033
+
2034
+ // src/storage/types.ts
2035
+ function isWorkersConfig(config) {
2036
+ return "bindings" in config;
2037
+ }
2038
+
2039
+ // src/storage/index.ts
2040
+ var CloudflareStore = class extends MastraStorage {
2041
+ stores;
2042
+ client;
2043
+ accountId;
2044
+ namespacePrefix;
2045
+ bindings;
2046
+ validateWorkersConfig(config) {
2047
+ if (!isWorkersConfig(config)) {
2048
+ throw new Error("Invalid Workers API configuration");
2049
+ }
2050
+ if (!config.bindings) {
2051
+ throw new Error("KV bindings are required when using Workers Binding API");
2052
+ }
2053
+ const requiredTables = [TABLE_THREADS, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_SCORERS];
2054
+ for (const table of requiredTables) {
2055
+ if (!(table in config.bindings)) {
2056
+ throw new Error(`Missing KV binding for table: ${table}`);
2057
+ }
2058
+ }
2059
+ }
2060
+ validateRestConfig(config) {
2061
+ if (isWorkersConfig(config)) {
2062
+ throw new Error("Invalid REST API configuration");
2063
+ }
2064
+ if (!config.accountId?.trim()) {
2065
+ throw new Error("accountId is required for REST API");
2066
+ }
2067
+ if (!config.apiToken?.trim()) {
2068
+ throw new Error("apiToken is required for REST API");
2069
+ }
2070
+ }
2071
+ get supports() {
2072
+ const supports = super.supports;
2073
+ supports.listScoresBySpan = true;
2074
+ supports.resourceWorkingMemory = true;
2075
+ supports.selectByIncludeResourceScope = true;
2076
+ return supports;
2077
+ }
2078
+ constructor(config) {
2079
+ super({ id: config.id, name: "Cloudflare" });
2080
+ try {
2081
+ if (isWorkersConfig(config)) {
2082
+ this.validateWorkersConfig(config);
2083
+ this.bindings = config.bindings;
2084
+ this.namespacePrefix = config.keyPrefix?.trim() || "";
2085
+ this.logger.info("Using Cloudflare KV Workers Binding API");
2086
+ } else {
2087
+ this.validateRestConfig(config);
2088
+ this.accountId = config.accountId.trim();
2089
+ this.namespacePrefix = config.namespacePrefix?.trim() || "";
2090
+ this.client = new Cloudflare({
2091
+ apiToken: config.apiToken.trim()
2092
+ });
2093
+ this.logger.info("Using Cloudflare KV REST API");
2094
+ }
2095
+ const operations = new StoreOperationsCloudflare({
2096
+ accountId: this.accountId,
2097
+ client: this.client,
2098
+ namespacePrefix: this.namespacePrefix,
2099
+ bindings: this.bindings
2100
+ });
2101
+ const workflows = new WorkflowsStorageCloudflare({
2102
+ operations
2103
+ });
2104
+ const memory = new MemoryStorageCloudflare({
2105
+ operations
2106
+ });
2107
+ const scores = new ScoresStorageCloudflare({
2108
+ operations
2109
+ });
2110
+ this.stores = {
2111
+ operations,
2112
+ workflows,
2113
+ memory,
2114
+ scores
2115
+ };
2116
+ } catch (error) {
2117
+ throw new MastraError(
2118
+ {
2119
+ id: "CLOUDFLARE_STORAGE_INIT_FAILED",
2120
+ domain: ErrorDomain.STORAGE,
2121
+ category: ErrorCategory.THIRD_PARTY
2122
+ },
2123
+ error
2124
+ );
2125
+ }
2126
+ }
2127
+ async createTable({
2128
+ tableName,
2129
+ schema
2130
+ }) {
2131
+ return this.stores.operations.createTable({ tableName, schema });
2132
+ }
2133
+ async alterTable(_args) {
2134
+ return this.stores.operations.alterTable(_args);
2135
+ }
2136
+ async clearTable({ tableName }) {
2137
+ return this.stores.operations.clearTable({ tableName });
2138
+ }
2139
+ async dropTable({ tableName }) {
2140
+ return this.stores.operations.dropTable({ tableName });
2141
+ }
2142
+ async insert({
2143
+ tableName,
2144
+ record
2145
+ }) {
2146
+ return this.stores.operations.insert({ tableName, record });
2147
+ }
2148
+ async load({ tableName, keys }) {
2149
+ return this.stores.operations.load({ tableName, keys });
2150
+ }
2151
+ async getThreadById({ threadId }) {
2152
+ return this.stores.memory.getThreadById({ threadId });
2153
+ }
2154
+ async saveThread({ thread }) {
2155
+ return this.stores.memory.saveThread({ thread });
2156
+ }
2157
+ async updateThread({
2158
+ id,
2159
+ title,
2160
+ metadata
2161
+ }) {
2162
+ return this.stores.memory.updateThread({ id, title, metadata });
2163
+ }
2164
+ async deleteThread({ threadId }) {
2165
+ return this.stores.memory.deleteThread({ threadId });
2166
+ }
2167
+ async saveMessages(args) {
2168
+ return this.stores.memory.saveMessages(args);
2169
+ }
2170
+ async updateWorkflowResults({
2171
+ workflowName,
2172
+ runId,
2173
+ stepId,
2174
+ result,
2175
+ requestContext
2176
+ }) {
2177
+ return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
2178
+ }
2179
+ async updateWorkflowState({
2180
+ workflowName,
2181
+ runId,
2182
+ opts
2183
+ }) {
2184
+ return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
2185
+ }
2186
+ async listMessagesById({ messageIds }) {
2187
+ return this.stores.memory.listMessagesById({ messageIds });
2188
+ }
2189
+ async persistWorkflowSnapshot(params) {
2190
+ return this.stores.workflows.persistWorkflowSnapshot(params);
2191
+ }
2192
+ async loadWorkflowSnapshot(params) {
2193
+ return this.stores.workflows.loadWorkflowSnapshot(params);
2194
+ }
2195
+ async batchInsert(input) {
2196
+ return this.stores.operations.batchInsert(input);
2197
+ }
2198
+ async listWorkflowRuns({
2199
+ workflowName,
2200
+ perPage = 20,
2201
+ page = 0,
2202
+ resourceId,
2203
+ fromDate,
2204
+ toDate,
2205
+ status
2206
+ } = {}) {
2207
+ return this.stores.workflows.listWorkflowRuns({
2208
+ workflowName,
2209
+ perPage,
2210
+ page,
2211
+ resourceId,
2212
+ fromDate,
2213
+ toDate,
2214
+ status
2215
+ });
2216
+ }
2217
+ async getWorkflowRunById({
2218
+ runId,
2219
+ workflowName
2220
+ }) {
2221
+ return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
2222
+ }
2223
+ async updateMessages(args) {
2224
+ return this.stores.memory.updateMessages(args);
2225
+ }
2226
+ async getScoreById({ id }) {
2227
+ return this.stores.scores.getScoreById({ id });
2228
+ }
2229
+ async saveScore(score) {
2230
+ return this.stores.scores.saveScore(score);
2231
+ }
2232
+ async listScoresByRunId({
2233
+ runId,
2234
+ pagination
2235
+ }) {
2236
+ return this.stores.scores.listScoresByRunId({ runId, pagination });
2237
+ }
2238
+ async listScoresByEntityId({
2239
+ entityId,
2240
+ entityType,
2241
+ pagination
2242
+ }) {
2243
+ return this.stores.scores.listScoresByEntityId({ entityId, entityType, pagination });
2244
+ }
2245
+ async listScoresByScorerId({
2246
+ scorerId,
2247
+ entityId,
2248
+ entityType,
2249
+ source,
2250
+ pagination
2251
+ }) {
2252
+ return this.stores.scores.listScoresByScorerId({ scorerId, entityId, entityType, source, pagination });
2253
+ }
2254
+ async listScoresBySpan({
2255
+ traceId,
2256
+ spanId,
2257
+ pagination
2258
+ }) {
2259
+ return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination });
2260
+ }
2261
+ async getResourceById({ resourceId }) {
2262
+ return this.stores.memory.getResourceById({ resourceId });
2263
+ }
2264
+ async saveResource({ resource }) {
2265
+ return this.stores.memory.saveResource({ resource });
2266
+ }
2267
+ async updateResource({
2268
+ resourceId,
2269
+ workingMemory,
2270
+ metadata
2271
+ }) {
2272
+ return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
2273
+ }
1159
2274
  async close() {
1160
2275
  }
1161
2276
  };
1162
2277
 
1163
2278
  export { CloudflareStore };
2279
+ //# sourceMappingURL=index.js.map
2280
+ //# sourceMappingURL=index.js.map