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

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