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