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