@mastra/cloudflare 0.0.0-error-handler-fix-20251020202607 → 0.0.0-execa-dynamic-import-20260304221256

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