@mastra/cloudflare 1.0.0-beta.0 → 1.0.0-beta.10

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