@mastra/cloudflare 1.6.0-alpha.0 → 1.6.0

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