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