@mastra/cloudflare-d1 1.2.0-alpha.0 → 1.2.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,1534 +1,1414 @@
1
- import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
2
- import { BackgroundTasksStorage, TABLE_SCHEMAS, TABLE_BACKGROUND_TASKS, MemoryStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES, ensureDate, createStorageErrorId, normalizePerPage, calculatePagination, validateStorageMetadataFilter, serializeDate, ScoresStorage, TABLE_SCORERS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, MastraCompositeStore, getSqlType, getDefaultValue, transformScoreRow as transformScoreRow$1 } from '@mastra/core/storage';
3
- import Cloudflare from 'cloudflare';
4
- import { MastraBase } from '@mastra/core/base';
5
- import { parseSqlIdentifier } from '@mastra/core/utils';
6
- import { MessageList } from '@mastra/core/agent';
7
- import { saveScorePayloadSchema } from '@mastra/core/evals';
8
-
9
- // src/storage/index.ts
10
-
11
- // src/storage/domains/utils.ts
1
+ import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
2
+ import { BackgroundTasksStorage, MastraCompositeStore, MemoryStorage, ScoresStorage, TABLE_BACKGROUND_TASKS, TABLE_MESSAGES, TABLE_RESOURCES, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_THREADS, TABLE_WORKFLOW_SNAPSHOT, WorkflowsStorage, calculatePagination, createStorageErrorId, ensureDate, getDefaultValue, getSqlType, normalizePerPage, serializeDate, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
3
+ import Cloudflare from "cloudflare";
4
+ import { MastraBase } from "@mastra/core/base";
5
+ import { parseSqlIdentifier } from "@mastra/core/utils";
6
+ import { MessageList } from "@mastra/core/agent";
7
+ import { saveScorePayloadSchema } from "@mastra/core/evals";
8
+ //#region src/storage/domains/utils.ts
12
9
  function isArrayOfRecords(value) {
13
- return value && Array.isArray(value) && value.length > 0;
10
+ return value && Array.isArray(value) && value.length > 0;
14
11
  }
15
12
  function deserializeValue(value, type) {
16
- if (value === null || value === void 0) return null;
17
- if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
18
- try {
19
- return JSON.parse(value);
20
- } catch {
21
- return value;
22
- }
23
- }
24
- return value;
13
+ if (value === null || value === void 0) return null;
14
+ if (type === "date" && typeof value === "string") return new Date(value);
15
+ if (type === "jsonb" && typeof value === "string") try {
16
+ return JSON.parse(value);
17
+ } catch {
18
+ return value;
19
+ }
20
+ if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) try {
21
+ return JSON.parse(value);
22
+ } catch {
23
+ return value;
24
+ }
25
+ return value;
25
26
  }
27
+ //#endregion
28
+ //#region src/storage/sql-builder.ts
29
+ /**
30
+ * SQL Builder class for constructing type-safe SQL queries
31
+ * This helps create maintainable and secure SQL queries with proper parameter handling
32
+ */
26
33
  var SqlBuilder = class {
27
- sql = "";
28
- params = [];
29
- whereAdded = false;
30
- // Basic query building
31
- select(columns) {
32
- if (!columns || Array.isArray(columns) && columns.length === 0) {
33
- this.sql = "SELECT *";
34
- } else {
35
- const cols = Array.isArray(columns) ? columns : [columns];
36
- const parsedCols = cols.map((col) => parseSelectIdentifier(col));
37
- this.sql = `SELECT ${parsedCols.join(", ")}`;
38
- }
39
- return this;
40
- }
41
- from(table) {
42
- const parsedTableName = parseSqlIdentifier(table, "table name");
43
- this.sql += ` FROM ${parsedTableName}`;
44
- return this;
45
- }
46
- /**
47
- * Add a WHERE clause to the query
48
- * @param condition The condition to add
49
- * @param params Parameters to bind to the condition
50
- */
51
- where(condition, ...params) {
52
- this.sql += ` WHERE ${condition}`;
53
- this.params.push(...params);
54
- this.whereAdded = true;
55
- return this;
56
- }
57
- /**
58
- * Add a WHERE clause if it hasn't been added yet, otherwise add an AND clause
59
- * @param condition The condition to add
60
- * @param params Parameters to bind to the condition
61
- */
62
- whereAnd(condition, ...params) {
63
- if (this.whereAdded) {
64
- return this.andWhere(condition, ...params);
65
- } else {
66
- return this.where(condition, ...params);
67
- }
68
- }
69
- andWhere(condition, ...params) {
70
- this.sql += ` AND ${condition}`;
71
- this.params.push(...params);
72
- return this;
73
- }
74
- orWhere(condition, ...params) {
75
- this.sql += ` OR ${condition}`;
76
- this.params.push(...params);
77
- return this;
78
- }
79
- orderBy(column, direction = "ASC") {
80
- const parsedColumn = parseSqlIdentifier(column, "column name");
81
- if (!["ASC", "DESC"].includes(direction)) {
82
- throw new Error(`Invalid sort direction: ${direction}`);
83
- }
84
- this.sql += ` ORDER BY ${parsedColumn} ${direction}`;
85
- return this;
86
- }
87
- limit(count) {
88
- this.sql += ` LIMIT ?`;
89
- this.params.push(count);
90
- return this;
91
- }
92
- offset(count) {
93
- this.sql += ` OFFSET ?`;
94
- this.params.push(count);
95
- return this;
96
- }
97
- count() {
98
- this.sql += "SELECT COUNT(*) AS count";
99
- return this;
100
- }
101
- /**
102
- * Insert a row, or update specific columns on conflict (upsert).
103
- * @param table Table name
104
- * @param columns Columns to insert
105
- * @param values Values to insert
106
- * @param conflictColumns Columns to check for conflict (usually PK or UNIQUE)
107
- * @param updateMap Object mapping columns to update to their new value (e.g. { name: 'excluded.name' })
108
- */
109
- insert(table, columns, values, conflictColumns, updateMap) {
110
- const parsedTableName = parseSqlIdentifier(table, "table name");
111
- const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
112
- const placeholders = parsedColumns.map(() => "?").join(", ");
113
- if (conflictColumns && updateMap) {
114
- const parsedConflictColumns = conflictColumns.map((col) => parseSqlIdentifier(col, "column name"));
115
- const updateClause = Object.entries(updateMap).map(([col, expr]) => `${col} = ${expr}`).join(", ");
116
- this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${parsedConflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
117
- this.params.push(...values);
118
- return this;
119
- }
120
- this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders})`;
121
- this.params.push(...values);
122
- return this;
123
- }
124
- // Update operations
125
- update(table, columns, values) {
126
- const parsedTableName = parseSqlIdentifier(table, "table name");
127
- const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
128
- const setClause = parsedColumns.map((col) => `${col} = ?`).join(", ");
129
- this.sql = `UPDATE ${parsedTableName} SET ${setClause}`;
130
- this.params.push(...values);
131
- return this;
132
- }
133
- // Delete operations
134
- delete(table) {
135
- const parsedTableName = parseSqlIdentifier(table, "table name");
136
- this.sql = `DELETE FROM ${parsedTableName}`;
137
- return this;
138
- }
139
- /**
140
- * Create a table if it doesn't exist
141
- * @param table The table name
142
- * @param columnDefinitions The column definitions as an array of strings
143
- * @param tableConstraints Optional constraints for the table
144
- * @returns The builder instance
145
- */
146
- createTable(table, columnDefinitions, tableConstraints) {
147
- const parsedTableName = parseSqlIdentifier(table, "table name");
148
- const parsedColumnDefinitions = columnDefinitions.map((def) => {
149
- const colName = def.split(/\s+/)[0];
150
- if (!colName) throw new Error("Empty column name in definition");
151
- parseSqlIdentifier(colName, "column name");
152
- return def;
153
- });
154
- const columns = parsedColumnDefinitions.join(", ");
155
- const constraints = tableConstraints && tableConstraints.length > 0 ? ", " + tableConstraints.join(", ") : "";
156
- this.sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns}${constraints})`;
157
- return this;
158
- }
159
- /**
160
- * Check if an index exists in the database
161
- * @param indexName The name of the index to check
162
- * @param tableName The table the index is on
163
- * @returns The builder instance
164
- */
165
- checkIndexExists(indexName, tableName) {
166
- this.sql = `SELECT name FROM sqlite_master WHERE type='index' AND name=? AND tbl_name=?`;
167
- this.params.push(indexName, tableName);
168
- return this;
169
- }
170
- /**
171
- * Create an index if it doesn't exist
172
- * @param indexName The name of the index to create
173
- * @param tableName The table to create the index on
174
- * @param columnName The column to index
175
- * @param indexType Optional index type (e.g., 'UNIQUE')
176
- * @returns The builder instance
177
- */
178
- createIndex(indexName, tableName, columnName, indexType = "") {
179
- const parsedIndexName = parseSqlIdentifier(indexName, "index name");
180
- const parsedTableName = parseSqlIdentifier(tableName, "table name");
181
- const parsedColumnName = parseSqlIdentifier(columnName, "column name");
182
- this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${parsedIndexName} ON ${parsedTableName}(${parsedColumnName})`;
183
- return this;
184
- }
185
- /**
186
- * Add a LIKE condition to the query
187
- * @param column The column to check
188
- * @param value The value to match (will be wrapped with % for LIKE)
189
- * @param exact If true, will not add % wildcards
190
- */
191
- like(column, value, exact = false) {
192
- const parsedColumnName = parseSqlIdentifier(column, "column name");
193
- const likeValue = exact ? value : `%${value}%`;
194
- if (this.whereAdded) {
195
- this.sql += ` AND ${parsedColumnName} LIKE ?`;
196
- } else {
197
- this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
198
- this.whereAdded = true;
199
- }
200
- this.params.push(likeValue);
201
- return this;
202
- }
203
- /**
204
- * Add a JSON LIKE condition for searching in JSON fields
205
- * @param column The JSON column to search in
206
- * @param key The JSON key to match
207
- * @param value The value to match
208
- */
209
- jsonLike(column, key, value) {
210
- const parsedColumnName = parseSqlIdentifier(column, "column name");
211
- const parsedKey = parseSqlIdentifier(key, "key name");
212
- const jsonPattern = `%"${parsedKey}":"${value}"%`;
213
- if (this.whereAdded) {
214
- this.sql += ` AND ${parsedColumnName} LIKE ?`;
215
- } else {
216
- this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
217
- this.whereAdded = true;
218
- }
219
- this.params.push(jsonPattern);
220
- return this;
221
- }
222
- /**
223
- * Get the built query
224
- * @returns Object containing the SQL string and parameters array
225
- */
226
- build() {
227
- return {
228
- sql: this.sql,
229
- params: this.params
230
- };
231
- }
232
- /**
233
- * Reset the builder for reuse
234
- * @returns The reset builder instance
235
- */
236
- reset() {
237
- this.sql = "";
238
- this.params = [];
239
- this.whereAdded = false;
240
- return this;
241
- }
34
+ sql = "";
35
+ params = [];
36
+ whereAdded = false;
37
+ select(columns) {
38
+ if (!columns || Array.isArray(columns) && columns.length === 0) this.sql = "SELECT *";
39
+ else {
40
+ const parsedCols = (Array.isArray(columns) ? columns : [columns]).map((col) => parseSelectIdentifier(col));
41
+ this.sql = `SELECT ${parsedCols.join(", ")}`;
42
+ }
43
+ return this;
44
+ }
45
+ from(table) {
46
+ const parsedTableName = parseSqlIdentifier(table, "table name");
47
+ this.sql += ` FROM ${parsedTableName}`;
48
+ return this;
49
+ }
50
+ /**
51
+ * Add a WHERE clause to the query
52
+ * @param condition The condition to add
53
+ * @param params Parameters to bind to the condition
54
+ */
55
+ where(condition, ...params) {
56
+ this.sql += ` WHERE ${condition}`;
57
+ this.params.push(...params);
58
+ this.whereAdded = true;
59
+ return this;
60
+ }
61
+ /**
62
+ * Add a WHERE clause if it hasn't been added yet, otherwise add an AND clause
63
+ * @param condition The condition to add
64
+ * @param params Parameters to bind to the condition
65
+ */
66
+ whereAnd(condition, ...params) {
67
+ if (this.whereAdded) return this.andWhere(condition, ...params);
68
+ else return this.where(condition, ...params);
69
+ }
70
+ andWhere(condition, ...params) {
71
+ this.sql += ` AND ${condition}`;
72
+ this.params.push(...params);
73
+ return this;
74
+ }
75
+ orWhere(condition, ...params) {
76
+ this.sql += ` OR ${condition}`;
77
+ this.params.push(...params);
78
+ return this;
79
+ }
80
+ orderBy(column, direction = "ASC") {
81
+ const parsedColumn = parseSqlIdentifier(column, "column name");
82
+ if (!["ASC", "DESC"].includes(direction)) throw new Error(`Invalid sort direction: ${direction}`);
83
+ this.sql += ` ORDER BY ${parsedColumn} ${direction}`;
84
+ return this;
85
+ }
86
+ limit(count) {
87
+ this.sql += ` LIMIT ?`;
88
+ this.params.push(count);
89
+ return this;
90
+ }
91
+ offset(count) {
92
+ this.sql += ` OFFSET ?`;
93
+ this.params.push(count);
94
+ return this;
95
+ }
96
+ count() {
97
+ this.sql += "SELECT COUNT(*) AS count";
98
+ return this;
99
+ }
100
+ /**
101
+ * Insert a row, or update specific columns on conflict (upsert).
102
+ * @param table Table name
103
+ * @param columns Columns to insert
104
+ * @param values Values to insert
105
+ * @param conflictColumns Columns to check for conflict (usually PK or UNIQUE)
106
+ * @param updateMap Object mapping columns to update to their new value (e.g. { name: 'excluded.name' })
107
+ */
108
+ insert(table, columns, values, conflictColumns, updateMap) {
109
+ const parsedTableName = parseSqlIdentifier(table, "table name");
110
+ const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
111
+ const placeholders = parsedColumns.map(() => "?").join(", ");
112
+ if (conflictColumns && updateMap) {
113
+ const parsedConflictColumns = conflictColumns.map((col) => parseSqlIdentifier(col, "column name"));
114
+ const updateClause = Object.entries(updateMap).map(([col, expr]) => `${col} = ${expr}`).join(", ");
115
+ this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${parsedConflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
116
+ this.params.push(...values);
117
+ return this;
118
+ }
119
+ this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders})`;
120
+ this.params.push(...values);
121
+ return this;
122
+ }
123
+ update(table, columns, values) {
124
+ const parsedTableName = parseSqlIdentifier(table, "table name");
125
+ const setClause = columns.map((col) => parseSqlIdentifier(col, "column name")).map((col) => `${col} = ?`).join(", ");
126
+ this.sql = `UPDATE ${parsedTableName} SET ${setClause}`;
127
+ this.params.push(...values);
128
+ return this;
129
+ }
130
+ delete(table) {
131
+ const parsedTableName = parseSqlIdentifier(table, "table name");
132
+ this.sql = `DELETE FROM ${parsedTableName}`;
133
+ return this;
134
+ }
135
+ /**
136
+ * Create a table if it doesn't exist
137
+ * @param table The table name
138
+ * @param columnDefinitions The column definitions as an array of strings
139
+ * @param tableConstraints Optional constraints for the table
140
+ * @returns The builder instance
141
+ */
142
+ createTable(table, columnDefinitions, tableConstraints) {
143
+ const parsedTableName = parseSqlIdentifier(table, "table name");
144
+ const columns = columnDefinitions.map((def) => {
145
+ const colName = def.split(/\s+/)[0];
146
+ if (!colName) throw new Error("Empty column name in definition");
147
+ parseSqlIdentifier(colName, "column name");
148
+ return def;
149
+ }).join(", ");
150
+ const constraints = tableConstraints && tableConstraints.length > 0 ? ", " + tableConstraints.join(", ") : "";
151
+ this.sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns}${constraints})`;
152
+ return this;
153
+ }
154
+ /**
155
+ * Check if an index exists in the database
156
+ * @param indexName The name of the index to check
157
+ * @param tableName The table the index is on
158
+ * @returns The builder instance
159
+ */
160
+ checkIndexExists(indexName, tableName) {
161
+ this.sql = `SELECT name FROM sqlite_master WHERE type='index' AND name=? AND tbl_name=?`;
162
+ this.params.push(indexName, tableName);
163
+ return this;
164
+ }
165
+ /**
166
+ * Create an index if it doesn't exist
167
+ * @param indexName The name of the index to create
168
+ * @param tableName The table to create the index on
169
+ * @param columnName The column to index
170
+ * @param indexType Optional index type (e.g., 'UNIQUE')
171
+ * @returns The builder instance
172
+ */
173
+ createIndex(indexName, tableName, columnName, indexType = "") {
174
+ const parsedIndexName = parseSqlIdentifier(indexName, "index name");
175
+ const parsedTableName = parseSqlIdentifier(tableName, "table name");
176
+ const parsedColumnName = parseSqlIdentifier(columnName, "column name");
177
+ this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${parsedIndexName} ON ${parsedTableName}(${parsedColumnName})`;
178
+ return this;
179
+ }
180
+ /**
181
+ * Add a LIKE condition to the query
182
+ * @param column The column to check
183
+ * @param value The value to match (will be wrapped with % for LIKE)
184
+ * @param exact If true, will not add % wildcards
185
+ */
186
+ like(column, value, exact = false) {
187
+ const parsedColumnName = parseSqlIdentifier(column, "column name");
188
+ const likeValue = exact ? value : `%${value}%`;
189
+ if (this.whereAdded) this.sql += ` AND ${parsedColumnName} LIKE ?`;
190
+ else {
191
+ this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
192
+ this.whereAdded = true;
193
+ }
194
+ this.params.push(likeValue);
195
+ return this;
196
+ }
197
+ /**
198
+ * Add a JSON LIKE condition for searching in JSON fields
199
+ * @param column The JSON column to search in
200
+ * @param key The JSON key to match
201
+ * @param value The value to match
202
+ */
203
+ jsonLike(column, key, value) {
204
+ const parsedColumnName = parseSqlIdentifier(column, "column name");
205
+ const jsonPattern = `%"${parseSqlIdentifier(key, "key name")}":"${value}"%`;
206
+ if (this.whereAdded) this.sql += ` AND ${parsedColumnName} LIKE ?`;
207
+ else {
208
+ this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
209
+ this.whereAdded = true;
210
+ }
211
+ this.params.push(jsonPattern);
212
+ return this;
213
+ }
214
+ /**
215
+ * Get the built query
216
+ * @returns Object containing the SQL string and parameters array
217
+ */
218
+ build() {
219
+ return {
220
+ sql: this.sql,
221
+ params: this.params
222
+ };
223
+ }
224
+ /**
225
+ * Reset the builder for reuse
226
+ * @returns The reset builder instance
227
+ */
228
+ reset() {
229
+ this.sql = "";
230
+ this.params = [];
231
+ this.whereAdded = false;
232
+ return this;
233
+ }
242
234
  };
243
235
  function createSqlBuilder() {
244
- return new SqlBuilder();
236
+ return new SqlBuilder();
245
237
  }
246
- var SQL_IDENTIFIER_PATTERN = /^[a-zA-Z0-9_]+(\s+AS\s+[a-zA-Z0-9_]+)?$/;
238
+ const SQL_IDENTIFIER_PATTERN = /^[a-zA-Z0-9_]+(\s+AS\s+[a-zA-Z0-9_]+)?$/;
239
+ /**
240
+ * Parses and returns a valid SQL SELECT column identifier.
241
+ * Allows a single identifier (letters, numbers, underscores), or '*', optionally with 'AS alias'.
242
+ *
243
+ * @param column - The column identifier string to parse.
244
+ * @returns The validated column identifier as a branded type.
245
+ * @throws {Error} If invalid.
246
+ *
247
+ * @example
248
+ * const col = parseSelectIdentifier('user_id'); // Ok
249
+ * parseSelectIdentifier('user_id AS uid'); // Ok
250
+ * parseSelectIdentifier('*'); // Ok
251
+ * parseSelectIdentifier('user id'); // Throws error
252
+ */
247
253
  function parseSelectIdentifier(column) {
248
- if (column !== "*" && !SQL_IDENTIFIER_PATTERN.test(column)) {
249
- throw new Error(
250
- `Invalid column name: "${column}". Must be "*" or a valid identifier (letters, numbers, underscores), optionally with "AS alias".`
251
- );
252
- }
253
- return column;
254
+ if (column !== "*" && !SQL_IDENTIFIER_PATTERN.test(column)) throw new Error(`Invalid column name: "${column}". Must be "*" or a valid identifier (letters, numbers, underscores), optionally with "AS alias".`);
255
+ return column;
254
256
  }
255
-
256
- // src/storage/db/index.ts
257
+ //#endregion
258
+ //#region src/storage/db/index.ts
259
+ /**
260
+ * Resolves D1DomainConfig to D1DBConfig.
261
+ * Handles creating a new D1 client if apiToken is provided.
262
+ */
257
263
  function resolveD1Config(config) {
258
- if ("client" in config) {
259
- return {
260
- client: config.client,
261
- tablePrefix: config.tablePrefix
262
- };
263
- }
264
- if ("binding" in config) {
265
- return {
266
- binding: config.binding,
267
- tablePrefix: config.tablePrefix
268
- };
269
- }
270
- const cfClient = new Cloudflare({ apiToken: config.apiToken });
271
- return {
272
- client: {
273
- query: ({ sql, params }) => {
274
- return cfClient.d1.database.query(config.databaseId, {
275
- account_id: config.accountId,
276
- sql,
277
- params
278
- });
279
- }
280
- },
281
- tablePrefix: config.tablePrefix
282
- };
264
+ if ("client" in config) return {
265
+ client: config.client,
266
+ tablePrefix: config.tablePrefix
267
+ };
268
+ if ("binding" in config) return {
269
+ binding: config.binding,
270
+ tablePrefix: config.tablePrefix
271
+ };
272
+ const cfClient = new Cloudflare({ apiToken: config.apiToken });
273
+ return {
274
+ client: { query: ({ sql, params }) => {
275
+ return cfClient.d1.database.query(config.databaseId, {
276
+ account_id: config.accountId,
277
+ sql,
278
+ params
279
+ });
280
+ } },
281
+ tablePrefix: config.tablePrefix
282
+ };
283
283
  }
284
284
  var D1DB = class extends MastraBase {
285
- client;
286
- binding;
287
- tablePrefix;
288
- /** Cache of actual table columns: tableName -> Promise<Set<columnName>> (stores in-flight promise to coalesce concurrent calls) */
289
- tableColumnsCache = /* @__PURE__ */ new Map();
290
- constructor(config) {
291
- super({
292
- component: "STORAGE",
293
- name: "D1_DB"
294
- });
295
- this.client = config.client;
296
- this.binding = config.binding;
297
- this.tablePrefix = config.tablePrefix || "";
298
- }
299
- async hasColumn(table, column) {
300
- const fullTableName = table.startsWith(this.tablePrefix) ? table : `${this.tablePrefix}${table}`;
301
- const sql = `PRAGMA table_info(${fullTableName});`;
302
- const result = await this.executeQuery({ sql, params: [] });
303
- if (!result || !Array.isArray(result)) return false;
304
- return result.some((col) => col.name === column || col.name === column.toLowerCase());
305
- }
306
- getTableName(tableName) {
307
- return `${this.tablePrefix}${tableName}`;
308
- }
309
- formatSqlParams(params) {
310
- return params.map((p) => p === void 0 || p === null ? null : p);
311
- }
312
- async executeWorkersBindingQuery({
313
- sql,
314
- params = [],
315
- first = false
316
- }) {
317
- if (!this.binding) {
318
- throw new Error("Workers binding is not configured");
319
- }
320
- try {
321
- const statement = this.binding.prepare(sql);
322
- const formattedParams = this.formatSqlParams(params);
323
- let result;
324
- if (formattedParams.length > 0) {
325
- if (first) {
326
- result = await statement.bind(...formattedParams).first();
327
- if (!result) return null;
328
- return result;
329
- } else {
330
- result = await statement.bind(...formattedParams).all();
331
- const results = result.results || [];
332
- return results;
333
- }
334
- } else {
335
- if (first) {
336
- result = await statement.first();
337
- if (!result) return null;
338
- return result;
339
- } else {
340
- result = await statement.all();
341
- const results = result.results || [];
342
- return results;
343
- }
344
- }
345
- } catch (error) {
346
- throw new MastraError(
347
- {
348
- id: createStorageErrorId("CLOUDFLARE_D1", "WORKERS_BINDING_QUERY", "FAILED"),
349
- domain: ErrorDomain.STORAGE,
350
- category: ErrorCategory.THIRD_PARTY,
351
- details: { sql }
352
- },
353
- error
354
- );
355
- }
356
- }
357
- async executeRestQuery({
358
- sql,
359
- params = [],
360
- first = false
361
- }) {
362
- if (!this.client) {
363
- throw new Error("D1 client is not configured");
364
- }
365
- try {
366
- const formattedParams = this.formatSqlParams(params);
367
- const response = await this.client.query({
368
- sql,
369
- params: formattedParams
370
- });
371
- const result = response.result || [];
372
- const results = result.flatMap((r) => r.results || []);
373
- if (first) {
374
- return results[0] || null;
375
- }
376
- return results;
377
- } catch (error) {
378
- throw new MastraError(
379
- {
380
- id: createStorageErrorId("CLOUDFLARE_D1", "REST_QUERY", "FAILED"),
381
- domain: ErrorDomain.STORAGE,
382
- category: ErrorCategory.THIRD_PARTY,
383
- details: { sql }
384
- },
385
- error
386
- );
387
- }
388
- }
389
- async executeQuery(options) {
390
- if (this.binding) {
391
- return this.executeWorkersBindingQuery(options);
392
- } else if (this.client) {
393
- return this.executeRestQuery(options);
394
- } else {
395
- throw new Error("Neither binding nor client is configured");
396
- }
397
- }
398
- /**
399
- * Gets the set of column names that actually exist in the database table.
400
- * Results are cached; the cache is invalidated when alterTable() adds new columns.
401
- */
402
- async getKnownColumnNames(tableName) {
403
- const cached = this.tableColumnsCache.get(tableName);
404
- if (cached) return cached;
405
- const promise = this.getTableColumns(tableName).then((columns) => {
406
- const names = new Set(columns.map((c) => c.name));
407
- if (names.size === 0) {
408
- this.tableColumnsCache.delete(tableName);
409
- }
410
- return names;
411
- });
412
- this.tableColumnsCache.set(tableName, promise);
413
- return promise;
414
- }
415
- /**
416
- * Filters a record to only include columns that exist in the actual database table.
417
- * Unknown columns are silently dropped to ensure forward compatibility.
418
- */
419
- async filterRecordToKnownColumns(tableName, record) {
420
- const knownColumns = await this.getKnownColumnNames(tableName);
421
- if (knownColumns.size === 0) return record;
422
- const filtered = {};
423
- for (const [key, value] of Object.entries(record)) {
424
- if (knownColumns.has(key)) {
425
- filtered[key] = value;
426
- }
427
- }
428
- return filtered;
429
- }
430
- async getTableColumns(tableName) {
431
- try {
432
- const sql = `PRAGMA table_info(${tableName})`;
433
- const result = await this.executeQuery({ sql });
434
- if (!result || !Array.isArray(result)) {
435
- return [];
436
- }
437
- return result.map((row) => ({
438
- name: row.name,
439
- type: row.type
440
- }));
441
- } catch (error) {
442
- this.logger.warn(`Failed to get table columns for ${tableName}:`, error);
443
- return [];
444
- }
445
- }
446
- serializeValue(value) {
447
- if (value === null || value === void 0) {
448
- return null;
449
- }
450
- if (value instanceof Date) {
451
- return value.toISOString();
452
- }
453
- if (typeof value === "object") {
454
- return JSON.stringify(value);
455
- }
456
- return value;
457
- }
458
- getSqlType(type) {
459
- switch (type) {
460
- case "bigint":
461
- return "INTEGER";
462
- // SQLite uses INTEGER for all integer sizes
463
- case "jsonb":
464
- return "TEXT";
465
- // Store JSON as TEXT in SQLite
466
- case "boolean":
467
- return "INTEGER";
468
- // SQLite uses 0/1 for booleans
469
- default:
470
- return getSqlType(type);
471
- }
472
- }
473
- getDefaultValue(type) {
474
- return getDefaultValue(type);
475
- }
476
- async createTable({
477
- tableName,
478
- schema
479
- }) {
480
- try {
481
- const fullTableName = this.getTableName(tableName);
482
- const columnDefinitions = Object.entries(schema).map(([colName, colDef]) => {
483
- const type = this.getSqlType(colDef.type);
484
- const nullable = colDef.nullable === false ? "NOT NULL" : "";
485
- const primaryKey = colDef.primaryKey ? "PRIMARY KEY" : "";
486
- return `${colName} ${type} ${nullable} ${primaryKey}`.trim();
487
- });
488
- const tableConstraints = [];
489
- if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
490
- tableConstraints.push("UNIQUE (workflow_name, run_id)");
491
- }
492
- const query = createSqlBuilder().createTable(fullTableName, columnDefinitions, tableConstraints);
493
- const { sql, params } = query.build();
494
- await this.executeQuery({ sql, params });
495
- this.logger.debug(`Created table ${fullTableName}`);
496
- this.tableColumnsCache.delete(fullTableName);
497
- } catch (error) {
498
- throw new MastraError(
499
- {
500
- id: createStorageErrorId("CLOUDFLARE_D1", "CREATE_TABLE", "FAILED"),
501
- domain: ErrorDomain.STORAGE,
502
- category: ErrorCategory.THIRD_PARTY,
503
- details: { tableName }
504
- },
505
- error
506
- );
507
- }
508
- }
509
- async clearTable({ tableName }) {
510
- try {
511
- const fullTableName = this.getTableName(tableName);
512
- const query = createSqlBuilder().delete(fullTableName);
513
- const { sql, params } = query.build();
514
- await this.executeQuery({ sql, params });
515
- this.logger.debug(`Cleared table ${fullTableName}`);
516
- } catch (error) {
517
- throw new MastraError(
518
- {
519
- id: createStorageErrorId("CLOUDFLARE_D1", "CLEAR_TABLE", "FAILED"),
520
- domain: ErrorDomain.STORAGE,
521
- category: ErrorCategory.THIRD_PARTY,
522
- details: { tableName }
523
- },
524
- error
525
- );
526
- }
527
- }
528
- async dropTable({ tableName }) {
529
- const fullTableName = this.getTableName(tableName);
530
- try {
531
- const sql = `DROP TABLE IF EXISTS ${fullTableName}`;
532
- await this.executeQuery({ sql });
533
- this.logger.debug(`Dropped table ${fullTableName}`);
534
- } catch (error) {
535
- throw new MastraError(
536
- {
537
- id: createStorageErrorId("CLOUDFLARE_D1", "DROP_TABLE", "FAILED"),
538
- domain: ErrorDomain.STORAGE,
539
- category: ErrorCategory.THIRD_PARTY,
540
- details: { tableName }
541
- },
542
- error
543
- );
544
- } finally {
545
- this.tableColumnsCache.delete(fullTableName);
546
- }
547
- }
548
- async alterTable(args) {
549
- const fullTableName = this.getTableName(args.tableName);
550
- try {
551
- const existingColumns = await this.getTableColumns(fullTableName);
552
- const existingColumnNames = new Set(existingColumns.map((col) => col.name));
553
- for (const [columnName, column] of Object.entries(args.schema)) {
554
- if (!existingColumnNames.has(columnName) && args.ifNotExists.includes(columnName)) {
555
- const sqlType = this.getSqlType(column.type);
556
- const defaultValue = this.getDefaultValue(column.type);
557
- const sql = `ALTER TABLE ${fullTableName} ADD COLUMN ${columnName} ${sqlType} ${defaultValue}`;
558
- await this.executeQuery({ sql });
559
- this.logger.debug(`Added column ${columnName} to table ${fullTableName}`);
560
- }
561
- }
562
- } catch (error) {
563
- throw new MastraError(
564
- {
565
- id: createStorageErrorId("CLOUDFLARE_D1", "ALTER_TABLE", "FAILED"),
566
- domain: ErrorDomain.STORAGE,
567
- category: ErrorCategory.THIRD_PARTY,
568
- details: { tableName: args.tableName }
569
- },
570
- error
571
- );
572
- } finally {
573
- this.tableColumnsCache.delete(fullTableName);
574
- }
575
- }
576
- async insert({ tableName, record }) {
577
- try {
578
- const fullTableName = this.getTableName(tableName);
579
- const processedRecord = await this.processRecord(record);
580
- const filteredRecord = await this.filterRecordToKnownColumns(fullTableName, processedRecord);
581
- const columns = Object.keys(filteredRecord);
582
- if (columns.length === 0) return;
583
- const values = Object.values(filteredRecord);
584
- const query = createSqlBuilder().insert(fullTableName, columns, values);
585
- const { sql, params } = query.build();
586
- await this.executeQuery({ sql, params });
587
- } catch (error) {
588
- throw new MastraError(
589
- {
590
- id: createStorageErrorId("CLOUDFLARE_D1", "INSERT", "FAILED"),
591
- domain: ErrorDomain.STORAGE,
592
- category: ErrorCategory.THIRD_PARTY,
593
- details: { tableName }
594
- },
595
- error
596
- );
597
- }
598
- }
599
- async batchInsert({ tableName, records }) {
600
- try {
601
- if (records.length === 0) return;
602
- const fullTableName = this.getTableName(tableName);
603
- const processedRecords = await Promise.all(records.map((record) => this.processRecord(record)));
604
- const filteredRecords = await Promise.all(
605
- processedRecords.map((r) => this.filterRecordToKnownColumns(fullTableName, r))
606
- );
607
- for (const record of filteredRecords) {
608
- const columns = Object.keys(record);
609
- if (columns.length === 0) continue;
610
- const values = Object.values(record);
611
- const query = createSqlBuilder().insert(fullTableName, columns, values);
612
- const { sql, params } = query.build();
613
- await this.executeQuery({ sql, params });
614
- }
615
- } catch (error) {
616
- throw new MastraError(
617
- {
618
- id: createStorageErrorId("CLOUDFLARE_D1", "BATCH_INSERT", "FAILED"),
619
- domain: ErrorDomain.STORAGE,
620
- category: ErrorCategory.THIRD_PARTY,
621
- details: { tableName }
622
- },
623
- error
624
- );
625
- }
626
- }
627
- async load({ tableName, keys }) {
628
- try {
629
- const fullTableName = this.getTableName(tableName);
630
- const query = createSqlBuilder().select("*").from(fullTableName);
631
- let firstKey = true;
632
- for (const [key, value] of Object.entries(keys)) {
633
- if (firstKey) {
634
- query.where(`${key} = ?`, value);
635
- firstKey = false;
636
- } else {
637
- query.andWhere(`${key} = ?`, value);
638
- }
639
- }
640
- query.orderBy("createdAt", "DESC");
641
- query.limit(1);
642
- const { sql, params } = query.build();
643
- const result = await this.executeQuery({ sql, params, first: true });
644
- if (!result) {
645
- return null;
646
- }
647
- const deserializedResult = {};
648
- for (const [key, value] of Object.entries(result)) {
649
- deserializedResult[key] = deserializeValue(value);
650
- }
651
- return deserializedResult;
652
- } catch (error) {
653
- throw new MastraError(
654
- {
655
- id: createStorageErrorId("CLOUDFLARE_D1", "LOAD", "FAILED"),
656
- domain: ErrorDomain.STORAGE,
657
- category: ErrorCategory.THIRD_PARTY,
658
- details: { tableName }
659
- },
660
- error
661
- );
662
- }
663
- }
664
- async processRecord(record) {
665
- const processed = {};
666
- for (const [key, value] of Object.entries(record)) {
667
- processed[key] = this.serializeValue(value);
668
- }
669
- return processed;
670
- }
671
- /**
672
- * Upsert multiple records in a batch operation
673
- * @param tableName The table to insert into
674
- * @param records The records to insert
675
- */
676
- async batchUpsert({ tableName, records }) {
677
- if (records.length === 0) return;
678
- const fullTableName = this.getTableName(tableName);
679
- try {
680
- const batchSize = 50;
681
- for (let i = 0; i < records.length; i += batchSize) {
682
- const batch = records.slice(i, i + batchSize);
683
- const recordsToInsert = batch;
684
- if (recordsToInsert.length > 0) {
685
- const filteredRecords = await Promise.all(
686
- recordsToInsert.map((r) => this.filterRecordToKnownColumns(fullTableName, r || {}))
687
- );
688
- for (const record of filteredRecords) {
689
- const columns = Object.keys(record);
690
- if (columns.length === 0) continue;
691
- const values = columns.map((col) => {
692
- const value = record[col];
693
- return this.serializeValue(value);
694
- });
695
- const recordToUpsert = columns.reduce(
696
- (acc, col) => {
697
- if (col !== "createdAt") acc[col] = `excluded.${col}`;
698
- return acc;
699
- },
700
- {}
701
- );
702
- const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], recordToUpsert);
703
- const { sql, params } = query.build();
704
- await this.executeQuery({ sql, params });
705
- }
706
- }
707
- this.logger.debug(
708
- `Processed batch ${Math.floor(i / batchSize) + 1} of ${Math.ceil(records.length / batchSize)}`
709
- );
710
- }
711
- this.logger.debug(`Successfully batch upserted ${records.length} records into ${tableName}`);
712
- } catch (error) {
713
- throw new MastraError(
714
- {
715
- id: createStorageErrorId("CLOUDFLARE_D1", "BATCH_UPSERT", "FAILED"),
716
- domain: ErrorDomain.STORAGE,
717
- category: ErrorCategory.THIRD_PARTY,
718
- text: `Failed to batch upsert into ${tableName}: ${error instanceof Error ? error.message : String(error)}`,
719
- details: { tableName }
720
- },
721
- error
722
- );
723
- }
724
- }
285
+ client;
286
+ binding;
287
+ tablePrefix;
288
+ /** Cache of actual table columns: tableName -> Promise<Set<columnName>> (stores in-flight promise to coalesce concurrent calls) */
289
+ tableColumnsCache = /* @__PURE__ */ new Map();
290
+ constructor(config) {
291
+ super({
292
+ component: "STORAGE",
293
+ name: "D1_DB"
294
+ });
295
+ this.client = config.client;
296
+ this.binding = config.binding;
297
+ this.tablePrefix = config.tablePrefix || "";
298
+ }
299
+ async hasColumn(table, column) {
300
+ const sql = `PRAGMA table_info(${table.startsWith(this.tablePrefix) ? table : `${this.tablePrefix}${table}`});`;
301
+ const result = await this.executeQuery({
302
+ sql,
303
+ params: []
304
+ });
305
+ if (!result || !Array.isArray(result)) return false;
306
+ return result.some((col) => col.name === column || col.name === column.toLowerCase());
307
+ }
308
+ getTableName(tableName) {
309
+ return `${this.tablePrefix}${tableName}`;
310
+ }
311
+ formatSqlParams(params) {
312
+ return params.map((p) => p === void 0 || p === null ? null : p);
313
+ }
314
+ async executeWorkersBindingQuery({ sql, params = [], first = false }) {
315
+ if (!this.binding) throw new Error("Workers binding is not configured");
316
+ try {
317
+ const statement = this.binding.prepare(sql);
318
+ const formattedParams = this.formatSqlParams(params);
319
+ let result;
320
+ if (formattedParams.length > 0) if (first) {
321
+ result = await statement.bind(...formattedParams).first();
322
+ if (!result) return null;
323
+ return result;
324
+ } else {
325
+ result = await statement.bind(...formattedParams).all();
326
+ return result.results || [];
327
+ }
328
+ else if (first) {
329
+ result = await statement.first();
330
+ if (!result) return null;
331
+ return result;
332
+ } else {
333
+ result = await statement.all();
334
+ return result.results || [];
335
+ }
336
+ } catch (error) {
337
+ throw new MastraError({
338
+ id: createStorageErrorId("CLOUDFLARE_D1", "WORKERS_BINDING_QUERY", "FAILED"),
339
+ domain: ErrorDomain.STORAGE,
340
+ category: ErrorCategory.THIRD_PARTY,
341
+ details: { sql }
342
+ }, error);
343
+ }
344
+ }
345
+ async executeRestQuery({ sql, params = [], first = false }) {
346
+ if (!this.client) throw new Error("D1 client is not configured");
347
+ try {
348
+ const formattedParams = this.formatSqlParams(params);
349
+ const results = ((await this.client.query({
350
+ sql,
351
+ params: formattedParams
352
+ })).result || []).flatMap((r) => r.results || []);
353
+ if (first) return results[0] || null;
354
+ return results;
355
+ } catch (error) {
356
+ throw new MastraError({
357
+ id: createStorageErrorId("CLOUDFLARE_D1", "REST_QUERY", "FAILED"),
358
+ domain: ErrorDomain.STORAGE,
359
+ category: ErrorCategory.THIRD_PARTY,
360
+ details: { sql }
361
+ }, error);
362
+ }
363
+ }
364
+ async executeQuery(options) {
365
+ if (this.binding) return this.executeWorkersBindingQuery(options);
366
+ else if (this.client) return this.executeRestQuery(options);
367
+ else throw new Error("Neither binding nor client is configured");
368
+ }
369
+ /**
370
+ * Gets the set of column names that actually exist in the database table.
371
+ * Results are cached; the cache is invalidated when alterTable() adds new columns.
372
+ */
373
+ async getKnownColumnNames(tableName) {
374
+ const cached = this.tableColumnsCache.get(tableName);
375
+ if (cached) return cached;
376
+ const promise = this.getTableColumns(tableName).then((columns) => {
377
+ const names = new Set(columns.map((c) => c.name));
378
+ if (names.size === 0) this.tableColumnsCache.delete(tableName);
379
+ return names;
380
+ });
381
+ this.tableColumnsCache.set(tableName, promise);
382
+ return promise;
383
+ }
384
+ /**
385
+ * Filters a record to only include columns that exist in the actual database table.
386
+ * Unknown columns are silently dropped to ensure forward compatibility.
387
+ */
388
+ async filterRecordToKnownColumns(tableName, record) {
389
+ const knownColumns = await this.getKnownColumnNames(tableName);
390
+ if (knownColumns.size === 0) return record;
391
+ const filtered = {};
392
+ for (const [key, value] of Object.entries(record)) if (knownColumns.has(key)) filtered[key] = value;
393
+ return filtered;
394
+ }
395
+ async getTableColumns(tableName) {
396
+ try {
397
+ const sql = `PRAGMA table_info(${tableName})`;
398
+ const result = await this.executeQuery({ sql });
399
+ if (!result || !Array.isArray(result)) return [];
400
+ return result.map((row) => ({
401
+ name: row.name,
402
+ type: row.type
403
+ }));
404
+ } catch (error) {
405
+ this.logger.warn(`Failed to get table columns for ${tableName}:`, error);
406
+ return [];
407
+ }
408
+ }
409
+ serializeValue(value) {
410
+ if (value === null || value === void 0) return null;
411
+ if (value instanceof Date) return value.toISOString();
412
+ if (typeof value === "object") return JSON.stringify(value);
413
+ return value;
414
+ }
415
+ getSqlType(type) {
416
+ switch (type) {
417
+ case "bigint": return "INTEGER";
418
+ case "jsonb": return "TEXT";
419
+ case "boolean": return "INTEGER";
420
+ default: return getSqlType(type);
421
+ }
422
+ }
423
+ getDefaultValue(type) {
424
+ return getDefaultValue(type);
425
+ }
426
+ async createTable({ tableName, schema }) {
427
+ try {
428
+ const fullTableName = this.getTableName(tableName);
429
+ const columnDefinitions = Object.entries(schema).map(([colName, colDef]) => {
430
+ return `${colName} ${this.getSqlType(colDef.type)} ${colDef.nullable === false ? "NOT NULL" : ""} ${colDef.primaryKey ? "PRIMARY KEY" : ""}`.trim();
431
+ });
432
+ const tableConstraints = [];
433
+ if (tableName === TABLE_WORKFLOW_SNAPSHOT) tableConstraints.push("UNIQUE (workflow_name, run_id)");
434
+ const { sql, params } = createSqlBuilder().createTable(fullTableName, columnDefinitions, tableConstraints).build();
435
+ await this.executeQuery({
436
+ sql,
437
+ params
438
+ });
439
+ this.logger.debug(`Created table ${fullTableName}`);
440
+ this.tableColumnsCache.delete(fullTableName);
441
+ } catch (error) {
442
+ throw new MastraError({
443
+ id: createStorageErrorId("CLOUDFLARE_D1", "CREATE_TABLE", "FAILED"),
444
+ domain: ErrorDomain.STORAGE,
445
+ category: ErrorCategory.THIRD_PARTY,
446
+ details: { tableName }
447
+ }, error);
448
+ }
449
+ }
450
+ async clearTable({ tableName }) {
451
+ try {
452
+ const fullTableName = this.getTableName(tableName);
453
+ const { sql, params } = createSqlBuilder().delete(fullTableName).build();
454
+ await this.executeQuery({
455
+ sql,
456
+ params
457
+ });
458
+ this.logger.debug(`Cleared table ${fullTableName}`);
459
+ } catch (error) {
460
+ throw new MastraError({
461
+ id: createStorageErrorId("CLOUDFLARE_D1", "CLEAR_TABLE", "FAILED"),
462
+ domain: ErrorDomain.STORAGE,
463
+ category: ErrorCategory.THIRD_PARTY,
464
+ details: { tableName }
465
+ }, error);
466
+ }
467
+ }
468
+ async dropTable({ tableName }) {
469
+ const fullTableName = this.getTableName(tableName);
470
+ try {
471
+ const sql = `DROP TABLE IF EXISTS ${fullTableName}`;
472
+ await this.executeQuery({ sql });
473
+ this.logger.debug(`Dropped table ${fullTableName}`);
474
+ } catch (error) {
475
+ throw new MastraError({
476
+ id: createStorageErrorId("CLOUDFLARE_D1", "DROP_TABLE", "FAILED"),
477
+ domain: ErrorDomain.STORAGE,
478
+ category: ErrorCategory.THIRD_PARTY,
479
+ details: { tableName }
480
+ }, error);
481
+ } finally {
482
+ this.tableColumnsCache.delete(fullTableName);
483
+ }
484
+ }
485
+ async alterTable(args) {
486
+ const fullTableName = this.getTableName(args.tableName);
487
+ try {
488
+ const existingColumns = await this.getTableColumns(fullTableName);
489
+ const existingColumnNames = new Set(existingColumns.map((col) => col.name));
490
+ for (const [columnName, column] of Object.entries(args.schema)) if (!existingColumnNames.has(columnName) && args.ifNotExists.includes(columnName)) {
491
+ const sql = `ALTER TABLE ${fullTableName} ADD COLUMN ${columnName} ${this.getSqlType(column.type)} ${this.getDefaultValue(column.type)}`;
492
+ await this.executeQuery({ sql });
493
+ this.logger.debug(`Added column ${columnName} to table ${fullTableName}`);
494
+ }
495
+ } catch (error) {
496
+ throw new MastraError({
497
+ id: createStorageErrorId("CLOUDFLARE_D1", "ALTER_TABLE", "FAILED"),
498
+ domain: ErrorDomain.STORAGE,
499
+ category: ErrorCategory.THIRD_PARTY,
500
+ details: { tableName: args.tableName }
501
+ }, error);
502
+ } finally {
503
+ this.tableColumnsCache.delete(fullTableName);
504
+ }
505
+ }
506
+ async insert({ tableName, record }) {
507
+ try {
508
+ const fullTableName = this.getTableName(tableName);
509
+ const processedRecord = await this.processRecord(record);
510
+ const filteredRecord = await this.filterRecordToKnownColumns(fullTableName, processedRecord);
511
+ const columns = Object.keys(filteredRecord);
512
+ if (columns.length === 0) return;
513
+ const values = Object.values(filteredRecord);
514
+ const { sql, params } = createSqlBuilder().insert(fullTableName, columns, values).build();
515
+ await this.executeQuery({
516
+ sql,
517
+ params
518
+ });
519
+ } catch (error) {
520
+ throw new MastraError({
521
+ id: createStorageErrorId("CLOUDFLARE_D1", "INSERT", "FAILED"),
522
+ domain: ErrorDomain.STORAGE,
523
+ category: ErrorCategory.THIRD_PARTY,
524
+ details: { tableName }
525
+ }, error);
526
+ }
527
+ }
528
+ async batchInsert({ tableName, records }) {
529
+ try {
530
+ if (records.length === 0) return;
531
+ const fullTableName = this.getTableName(tableName);
532
+ const processedRecords = await Promise.all(records.map((record) => this.processRecord(record)));
533
+ const filteredRecords = await Promise.all(processedRecords.map((r) => this.filterRecordToKnownColumns(fullTableName, r)));
534
+ for (const record of filteredRecords) {
535
+ const columns = Object.keys(record);
536
+ if (columns.length === 0) continue;
537
+ const values = Object.values(record);
538
+ const { sql, params } = createSqlBuilder().insert(fullTableName, columns, values).build();
539
+ await this.executeQuery({
540
+ sql,
541
+ params
542
+ });
543
+ }
544
+ } catch (error) {
545
+ throw new MastraError({
546
+ id: createStorageErrorId("CLOUDFLARE_D1", "BATCH_INSERT", "FAILED"),
547
+ domain: ErrorDomain.STORAGE,
548
+ category: ErrorCategory.THIRD_PARTY,
549
+ details: { tableName }
550
+ }, error);
551
+ }
552
+ }
553
+ async load({ tableName, keys }) {
554
+ try {
555
+ const fullTableName = this.getTableName(tableName);
556
+ const query = createSqlBuilder().select("*").from(fullTableName);
557
+ let firstKey = true;
558
+ for (const [key, value] of Object.entries(keys)) if (firstKey) {
559
+ query.where(`${key} = ?`, value);
560
+ firstKey = false;
561
+ } else query.andWhere(`${key} = ?`, value);
562
+ query.orderBy("createdAt", "DESC");
563
+ query.limit(1);
564
+ const { sql, params } = query.build();
565
+ const result = await this.executeQuery({
566
+ sql,
567
+ params,
568
+ first: true
569
+ });
570
+ if (!result) return null;
571
+ const deserializedResult = {};
572
+ for (const [key, value] of Object.entries(result)) deserializedResult[key] = deserializeValue(value);
573
+ return deserializedResult;
574
+ } catch (error) {
575
+ throw new MastraError({
576
+ id: createStorageErrorId("CLOUDFLARE_D1", "LOAD", "FAILED"),
577
+ domain: ErrorDomain.STORAGE,
578
+ category: ErrorCategory.THIRD_PARTY,
579
+ details: { tableName }
580
+ }, error);
581
+ }
582
+ }
583
+ async processRecord(record) {
584
+ const processed = {};
585
+ for (const [key, value] of Object.entries(record)) processed[key] = this.serializeValue(value);
586
+ return processed;
587
+ }
588
+ /**
589
+ * Upsert multiple records in a batch operation
590
+ * @param tableName The table to insert into
591
+ * @param records The records to insert
592
+ */
593
+ async batchUpsert({ tableName, records }) {
594
+ if (records.length === 0) return;
595
+ const fullTableName = this.getTableName(tableName);
596
+ try {
597
+ const batchSize = 50;
598
+ for (let i = 0; i < records.length; i += batchSize) {
599
+ const recordsToInsert = records.slice(i, i + batchSize);
600
+ if (recordsToInsert.length > 0) {
601
+ const filteredRecords = await Promise.all(recordsToInsert.map((r) => this.filterRecordToKnownColumns(fullTableName, r || {})));
602
+ for (const record of filteredRecords) {
603
+ const columns = Object.keys(record);
604
+ if (columns.length === 0) continue;
605
+ const values = columns.map((col) => {
606
+ const value = record[col];
607
+ return this.serializeValue(value);
608
+ });
609
+ const recordToUpsert = columns.reduce((acc, col) => {
610
+ if (col !== "createdAt") acc[col] = `excluded.${col}`;
611
+ return acc;
612
+ }, {});
613
+ const { sql, params } = createSqlBuilder().insert(fullTableName, columns, values, ["id"], recordToUpsert).build();
614
+ await this.executeQuery({
615
+ sql,
616
+ params
617
+ });
618
+ }
619
+ }
620
+ this.logger.debug(`Processed batch ${Math.floor(i / batchSize) + 1} of ${Math.ceil(records.length / batchSize)}`);
621
+ }
622
+ this.logger.debug(`Successfully batch upserted ${records.length} records into ${tableName}`);
623
+ } catch (error) {
624
+ throw new MastraError({
625
+ id: createStorageErrorId("CLOUDFLARE_D1", "BATCH_UPSERT", "FAILED"),
626
+ domain: ErrorDomain.STORAGE,
627
+ category: ErrorCategory.THIRD_PARTY,
628
+ text: `Failed to batch upsert into ${tableName}: ${error instanceof Error ? error.message : String(error)}`,
629
+ details: { tableName }
630
+ }, error);
631
+ }
632
+ }
725
633
  };
726
-
727
- // src/storage/domains/background-tasks/index.ts
634
+ //#endregion
635
+ //#region src/storage/domains/background-tasks/index.ts
728
636
  function serializeJson(v) {
729
- if (typeof v === "object" && v != null) return JSON.stringify(v);
730
- return v ?? null;
637
+ if (typeof v === "object" && v != null) return JSON.stringify(v);
638
+ return v ?? null;
731
639
  }
732
640
  function rowToTask(row) {
733
- const parseJson = (val) => {
734
- if (val == null || val === "") return void 0;
735
- if (typeof val === "string") {
736
- try {
737
- return JSON.parse(val);
738
- } catch {
739
- return val;
740
- }
741
- }
742
- return val;
743
- };
744
- return {
745
- id: row.id,
746
- status: row.status,
747
- toolName: row.tool_name,
748
- toolCallId: row.tool_call_id,
749
- args: parseJson(row.args) ?? {},
750
- agentId: row.agent_id,
751
- threadId: row.thread_id || void 0,
752
- resourceId: row.resource_id || void 0,
753
- runId: row.run_id ?? "",
754
- result: parseJson(row.result),
755
- error: parseJson(row.error),
756
- suspendPayload: parseJson(row.suspend_payload),
757
- retryCount: Number(row.retry_count ?? 0),
758
- maxRetries: Number(row.max_retries ?? 0),
759
- timeoutMs: Number(row.timeout_ms ?? 3e5),
760
- createdAt: new Date(row.createdAt),
761
- startedAt: row.startedAt ? new Date(row.startedAt) : void 0,
762
- suspendedAt: row.suspendedAt ? new Date(row.suspendedAt) : void 0,
763
- completedAt: row.completedAt ? new Date(row.completedAt) : void 0
764
- };
641
+ const parseJson = (val) => {
642
+ if (val == null || val === "") return void 0;
643
+ if (typeof val === "string") try {
644
+ return JSON.parse(val);
645
+ } catch {
646
+ return val;
647
+ }
648
+ return val;
649
+ };
650
+ return {
651
+ id: row.id,
652
+ status: row.status,
653
+ toolName: row.tool_name,
654
+ toolCallId: row.tool_call_id,
655
+ args: parseJson(row.args) ?? {},
656
+ agentId: row.agent_id,
657
+ threadId: row.thread_id || void 0,
658
+ resourceId: row.resource_id || void 0,
659
+ runId: row.run_id ?? "",
660
+ result: parseJson(row.result),
661
+ error: parseJson(row.error),
662
+ suspendPayload: parseJson(row.suspend_payload),
663
+ retryCount: Number(row.retry_count ?? 0),
664
+ maxRetries: Number(row.max_retries ?? 0),
665
+ timeoutMs: Number(row.timeout_ms ?? 3e5),
666
+ createdAt: new Date(row.createdAt),
667
+ startedAt: row.startedAt ? new Date(row.startedAt) : void 0,
668
+ suspendedAt: row.suspendedAt ? new Date(row.suspendedAt) : void 0,
669
+ completedAt: row.completedAt ? new Date(row.completedAt) : void 0
670
+ };
765
671
  }
766
672
  var BackgroundTasksStorageD1 = class extends BackgroundTasksStorage {
767
- #db;
768
- constructor(config) {
769
- super();
770
- const resolved = resolveD1Config(config);
771
- this.#db = new D1DB(resolved);
772
- }
773
- async init() {
774
- await this.#db.createTable({ tableName: TABLE_BACKGROUND_TASKS, schema: TABLE_SCHEMAS[TABLE_BACKGROUND_TASKS] });
775
- await this.#db.alterTable({
776
- tableName: TABLE_BACKGROUND_TASKS,
777
- schema: TABLE_SCHEMAS[TABLE_BACKGROUND_TASKS],
778
- ifNotExists: ["suspend_payload", "suspendedAt"]
779
- });
780
- }
781
- async dangerouslyClearAll() {
782
- await this.#db.clearTable({ tableName: TABLE_BACKGROUND_TASKS });
783
- }
784
- async createTask(task) {
785
- const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
786
- const { sql, params } = createSqlBuilder().insert(
787
- fullTableName,
788
- [
789
- "id",
790
- "tool_call_id",
791
- "tool_name",
792
- "agent_id",
793
- "thread_id",
794
- "resource_id",
795
- "run_id",
796
- "status",
797
- "args",
798
- "result",
799
- "error",
800
- "suspend_payload",
801
- "retry_count",
802
- "max_retries",
803
- "timeout_ms",
804
- "createdAt",
805
- "startedAt",
806
- "suspendedAt",
807
- "completedAt"
808
- ],
809
- [
810
- task.id,
811
- task.toolCallId,
812
- task.toolName,
813
- task.agentId,
814
- task.threadId ?? null,
815
- task.resourceId ?? null,
816
- task.runId,
817
- task.status,
818
- serializeJson(task.args),
819
- serializeJson(task.result),
820
- serializeJson(task.error),
821
- serializeJson(task.suspendPayload),
822
- task.retryCount,
823
- task.maxRetries,
824
- task.timeoutMs,
825
- task.createdAt.toISOString(),
826
- task.startedAt?.toISOString() ?? null,
827
- task.suspendedAt?.toISOString() ?? null,
828
- task.completedAt?.toISOString() ?? null
829
- ]
830
- ).build();
831
- await this.#db.executeQuery({ sql, params });
832
- }
833
- async updateTask(taskId, update) {
834
- const sets = [];
835
- const params = [];
836
- if ("status" in update) {
837
- sets.push("status = ?");
838
- params.push(update.status);
839
- }
840
- if ("result" in update) {
841
- sets.push("result = ?");
842
- params.push(serializeJson(update.result));
843
- }
844
- if ("error" in update) {
845
- sets.push("error = ?");
846
- params.push(serializeJson(update.error));
847
- }
848
- if ("suspendPayload" in update) {
849
- sets.push("suspend_payload = ?");
850
- params.push(serializeJson(update.suspendPayload));
851
- }
852
- if ("retryCount" in update) {
853
- sets.push("retry_count = ?");
854
- params.push(update.retryCount);
855
- }
856
- if ("startedAt" in update) {
857
- sets.push("startedAt = ?");
858
- params.push(update.startedAt?.toISOString() ?? null);
859
- }
860
- if ("suspendedAt" in update) {
861
- sets.push("suspendedAt = ?");
862
- params.push(update.suspendedAt?.toISOString() ?? null);
863
- }
864
- if ("completedAt" in update) {
865
- sets.push("completedAt = ?");
866
- params.push(update.completedAt?.toISOString() ?? null);
867
- }
868
- if (sets.length === 0) return;
869
- params.push(taskId);
870
- const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
871
- await this.#db.executeQuery({
872
- sql: `UPDATE ${fullTableName} SET ${sets.join(", ")} WHERE id = ?`,
873
- params
874
- });
875
- }
876
- async getTask(taskId) {
877
- const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
878
- const { sql, params } = createSqlBuilder().select("*").from(fullTableName).where("id = ?", taskId).build();
879
- const row = await this.#db.executeQuery({ sql, params, first: true });
880
- return row ? rowToTask(row) : null;
881
- }
882
- async listTasks(filter) {
883
- const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
884
- let builder = createSqlBuilder().select("*").from(fullTableName);
885
- let countBuilder = createSqlBuilder().count().from(fullTableName);
886
- if (filter.status) {
887
- const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
888
- builder = builder.where(`status IN (${statuses.map(() => "?").join(",")})`, ...statuses);
889
- countBuilder = countBuilder.where(`status IN (${statuses.map(() => "?").join(",")})`, ...statuses);
890
- }
891
- if (filter.agentId) {
892
- builder = builder.whereAnd("agent_id = ?", filter.agentId);
893
- countBuilder = countBuilder.whereAnd("agent_id = ?", filter.agentId);
894
- }
895
- if (filter.threadId) {
896
- builder = builder.whereAnd("thread_id = ?", filter.threadId);
897
- countBuilder = countBuilder.whereAnd("thread_id = ?", filter.threadId);
898
- }
899
- if (filter.runId) {
900
- builder = builder.whereAnd("run_id = ?", filter.runId);
901
- countBuilder = countBuilder.whereAnd("run_id = ?", filter.runId);
902
- }
903
- if (filter.toolName) {
904
- builder = builder.whereAnd("tool_name = ?", filter.toolName);
905
- countBuilder = countBuilder.whereAnd("tool_name = ?", filter.toolName);
906
- }
907
- if (filter.toolCallId) {
908
- builder = builder.whereAnd("tool_call_id = ?", filter.toolCallId);
909
- countBuilder = countBuilder.whereAnd("tool_call_id = ?", filter.toolCallId);
910
- }
911
- const dateCol = filter.dateFilterBy === "startedAt" ? "startedAt" : filter.dateFilterBy === "suspendedAt" ? "suspendedAt" : filter.dateFilterBy === "completedAt" ? "completedAt" : "createdAt";
912
- if (filter.fromDate) {
913
- builder = builder.whereAnd(`${dateCol} >= ?`, filter.fromDate.toISOString());
914
- countBuilder = countBuilder.whereAnd(`${dateCol} >= ?`, filter.fromDate.toISOString());
915
- }
916
- if (filter.toDate) {
917
- builder = builder.whereAnd(`${dateCol} < ?`, filter.toDate.toISOString());
918
- countBuilder = countBuilder.whereAnd(`${dateCol} < ?`, filter.toDate.toISOString());
919
- }
920
- const { sql: countSql, params: countParams } = countBuilder.build();
921
- const countRow = await this.#db.executeQuery({ sql: countSql, params: countParams, first: true });
922
- const total = Number(countRow?.count ?? 0);
923
- const orderCol = filter.orderBy === "startedAt" ? "startedAt" : filter.orderBy === "suspendedAt" ? "suspendedAt" : filter.orderBy === "completedAt" ? "completedAt" : "createdAt";
924
- builder = builder.orderBy(orderCol, filter.orderDirection === "desc" ? "DESC" : "ASC");
925
- if (filter.perPage != null) {
926
- builder = builder.limit(filter.perPage);
927
- if (filter.page != null) {
928
- builder = builder.offset(filter.page * filter.perPage);
929
- }
930
- }
931
- const { sql, params } = builder.build();
932
- const rows = await this.#db.executeQuery({ sql, params });
933
- return { tasks: rows.map(rowToTask), total };
934
- }
935
- async deleteTask(taskId) {
936
- const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
937
- await this.#db.executeQuery({ sql: `DELETE FROM ${fullTableName} WHERE id = ?`, params: [taskId] });
938
- }
939
- async deleteTasks(filter) {
940
- const conditions = [];
941
- const params = [];
942
- if (filter.status) {
943
- const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
944
- conditions.push(`status IN (${statuses.map(() => "?").join(",")})`);
945
- params.push(...statuses);
946
- }
947
- const dateCol = filter.dateFilterBy === "startedAt" ? "startedAt" : filter.dateFilterBy === "suspendedAt" ? "suspendedAt" : filter.dateFilterBy === "completedAt" ? "completedAt" : "createdAt";
948
- if (filter.fromDate) {
949
- conditions.push(`${dateCol} >= ?`);
950
- params.push(filter.fromDate.toISOString());
951
- }
952
- if (filter.toDate) {
953
- conditions.push(`${dateCol} < ?`);
954
- params.push(filter.toDate.toISOString());
955
- }
956
- if (filter.agentId) {
957
- conditions.push("agent_id = ?");
958
- params.push(filter.agentId);
959
- }
960
- if (filter.runId) {
961
- conditions.push("run_id = ?");
962
- params.push(filter.runId);
963
- }
964
- if (filter.toolCallId) {
965
- conditions.push("tool_call_id = ?");
966
- params.push(filter.toolCallId);
967
- }
968
- if (conditions.length === 0) return;
969
- const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
970
- await this.#db.executeQuery({
971
- sql: `DELETE FROM ${fullTableName} WHERE ${conditions.join(" AND ")}`,
972
- params
973
- });
974
- }
975
- async getRunningCount() {
976
- const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
977
- const row = await this.#db.executeQuery({
978
- sql: `SELECT COUNT(*) as count FROM ${fullTableName} WHERE status = 'running'`,
979
- params: [],
980
- first: true
981
- });
982
- return Number(row?.count ?? 0);
983
- }
984
- async getRunningCountByAgent(agentId) {
985
- const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
986
- const row = await this.#db.executeQuery({
987
- sql: `SELECT COUNT(*) as count FROM ${fullTableName} WHERE status = 'running' AND agent_id = ?`,
988
- params: [agentId],
989
- first: true
990
- });
991
- return Number(row?.count ?? 0);
992
- }
673
+ #db;
674
+ constructor(config) {
675
+ super();
676
+ const resolved = resolveD1Config(config);
677
+ this.#db = new D1DB(resolved);
678
+ }
679
+ async init() {
680
+ await this.#db.createTable({
681
+ tableName: TABLE_BACKGROUND_TASKS,
682
+ schema: TABLE_SCHEMAS[TABLE_BACKGROUND_TASKS]
683
+ });
684
+ await this.#db.alterTable({
685
+ tableName: TABLE_BACKGROUND_TASKS,
686
+ schema: TABLE_SCHEMAS[TABLE_BACKGROUND_TASKS],
687
+ ifNotExists: ["suspend_payload", "suspendedAt"]
688
+ });
689
+ }
690
+ async dangerouslyClearAll() {
691
+ await this.#db.clearTable({ tableName: TABLE_BACKGROUND_TASKS });
692
+ }
693
+ async createTask(task) {
694
+ const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
695
+ const { sql, params } = createSqlBuilder().insert(fullTableName, [
696
+ "id",
697
+ "tool_call_id",
698
+ "tool_name",
699
+ "agent_id",
700
+ "thread_id",
701
+ "resource_id",
702
+ "run_id",
703
+ "status",
704
+ "args",
705
+ "result",
706
+ "error",
707
+ "suspend_payload",
708
+ "retry_count",
709
+ "max_retries",
710
+ "timeout_ms",
711
+ "createdAt",
712
+ "startedAt",
713
+ "suspendedAt",
714
+ "completedAt"
715
+ ], [
716
+ task.id,
717
+ task.toolCallId,
718
+ task.toolName,
719
+ task.agentId,
720
+ task.threadId ?? null,
721
+ task.resourceId ?? null,
722
+ task.runId,
723
+ task.status,
724
+ serializeJson(task.args),
725
+ serializeJson(task.result),
726
+ serializeJson(task.error),
727
+ serializeJson(task.suspendPayload),
728
+ task.retryCount,
729
+ task.maxRetries,
730
+ task.timeoutMs,
731
+ task.createdAt.toISOString(),
732
+ task.startedAt?.toISOString() ?? null,
733
+ task.suspendedAt?.toISOString() ?? null,
734
+ task.completedAt?.toISOString() ?? null
735
+ ]).build();
736
+ await this.#db.executeQuery({
737
+ sql,
738
+ params
739
+ });
740
+ }
741
+ async updateTask(taskId, update) {
742
+ const sets = [];
743
+ const params = [];
744
+ if ("status" in update) {
745
+ sets.push("status = ?");
746
+ params.push(update.status);
747
+ }
748
+ if ("result" in update) {
749
+ sets.push("result = ?");
750
+ params.push(serializeJson(update.result));
751
+ }
752
+ if ("error" in update) {
753
+ sets.push("error = ?");
754
+ params.push(serializeJson(update.error));
755
+ }
756
+ if ("suspendPayload" in update) {
757
+ sets.push("suspend_payload = ?");
758
+ params.push(serializeJson(update.suspendPayload));
759
+ }
760
+ if ("retryCount" in update) {
761
+ sets.push("retry_count = ?");
762
+ params.push(update.retryCount);
763
+ }
764
+ if ("startedAt" in update) {
765
+ sets.push("startedAt = ?");
766
+ params.push(update.startedAt?.toISOString() ?? null);
767
+ }
768
+ if ("suspendedAt" in update) {
769
+ sets.push("suspendedAt = ?");
770
+ params.push(update.suspendedAt?.toISOString() ?? null);
771
+ }
772
+ if ("completedAt" in update) {
773
+ sets.push("completedAt = ?");
774
+ params.push(update.completedAt?.toISOString() ?? null);
775
+ }
776
+ if (sets.length === 0) return;
777
+ params.push(taskId);
778
+ const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
779
+ await this.#db.executeQuery({
780
+ sql: `UPDATE ${fullTableName} SET ${sets.join(", ")} WHERE id = ?`,
781
+ params
782
+ });
783
+ }
784
+ async getTask(taskId) {
785
+ const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
786
+ const { sql, params } = createSqlBuilder().select("*").from(fullTableName).where("id = ?", taskId).build();
787
+ const row = await this.#db.executeQuery({
788
+ sql,
789
+ params,
790
+ first: true
791
+ });
792
+ return row ? rowToTask(row) : null;
793
+ }
794
+ async listTasks(filter) {
795
+ const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
796
+ let builder = createSqlBuilder().select("*").from(fullTableName);
797
+ let countBuilder = createSqlBuilder().count().from(fullTableName);
798
+ if (filter.status) {
799
+ const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
800
+ builder = builder.where(`status IN (${statuses.map(() => "?").join(",")})`, ...statuses);
801
+ countBuilder = countBuilder.where(`status IN (${statuses.map(() => "?").join(",")})`, ...statuses);
802
+ }
803
+ if (filter.agentId) {
804
+ builder = builder.whereAnd("agent_id = ?", filter.agentId);
805
+ countBuilder = countBuilder.whereAnd("agent_id = ?", filter.agentId);
806
+ }
807
+ if (filter.threadId) {
808
+ builder = builder.whereAnd("thread_id = ?", filter.threadId);
809
+ countBuilder = countBuilder.whereAnd("thread_id = ?", filter.threadId);
810
+ }
811
+ if (filter.runId) {
812
+ builder = builder.whereAnd("run_id = ?", filter.runId);
813
+ countBuilder = countBuilder.whereAnd("run_id = ?", filter.runId);
814
+ }
815
+ if (filter.toolName) {
816
+ builder = builder.whereAnd("tool_name = ?", filter.toolName);
817
+ countBuilder = countBuilder.whereAnd("tool_name = ?", filter.toolName);
818
+ }
819
+ if (filter.toolCallId) {
820
+ builder = builder.whereAnd("tool_call_id = ?", filter.toolCallId);
821
+ countBuilder = countBuilder.whereAnd("tool_call_id = ?", filter.toolCallId);
822
+ }
823
+ const dateCol = filter.dateFilterBy === "startedAt" ? "startedAt" : filter.dateFilterBy === "suspendedAt" ? "suspendedAt" : filter.dateFilterBy === "completedAt" ? "completedAt" : "createdAt";
824
+ if (filter.fromDate) {
825
+ builder = builder.whereAnd(`${dateCol} >= ?`, filter.fromDate.toISOString());
826
+ countBuilder = countBuilder.whereAnd(`${dateCol} >= ?`, filter.fromDate.toISOString());
827
+ }
828
+ if (filter.toDate) {
829
+ builder = builder.whereAnd(`${dateCol} < ?`, filter.toDate.toISOString());
830
+ countBuilder = countBuilder.whereAnd(`${dateCol} < ?`, filter.toDate.toISOString());
831
+ }
832
+ const { sql: countSql, params: countParams } = countBuilder.build();
833
+ const countRow = await this.#db.executeQuery({
834
+ sql: countSql,
835
+ params: countParams,
836
+ first: true
837
+ });
838
+ const total = Number(countRow?.count ?? 0);
839
+ const orderCol = filter.orderBy === "startedAt" ? "startedAt" : filter.orderBy === "suspendedAt" ? "suspendedAt" : filter.orderBy === "completedAt" ? "completedAt" : "createdAt";
840
+ builder = builder.orderBy(orderCol, filter.orderDirection === "desc" ? "DESC" : "ASC");
841
+ if (filter.perPage != null) {
842
+ builder = builder.limit(filter.perPage);
843
+ if (filter.page != null) builder = builder.offset(filter.page * filter.perPage);
844
+ }
845
+ const { sql, params } = builder.build();
846
+ return {
847
+ tasks: (await this.#db.executeQuery({
848
+ sql,
849
+ params
850
+ })).map(rowToTask),
851
+ total
852
+ };
853
+ }
854
+ async deleteTask(taskId) {
855
+ const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
856
+ await this.#db.executeQuery({
857
+ sql: `DELETE FROM ${fullTableName} WHERE id = ?`,
858
+ params: [taskId]
859
+ });
860
+ }
861
+ async deleteTasks(filter) {
862
+ const conditions = [];
863
+ const params = [];
864
+ if (filter.status) {
865
+ const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
866
+ conditions.push(`status IN (${statuses.map(() => "?").join(",")})`);
867
+ params.push(...statuses);
868
+ }
869
+ const dateCol = filter.dateFilterBy === "startedAt" ? "startedAt" : filter.dateFilterBy === "suspendedAt" ? "suspendedAt" : filter.dateFilterBy === "completedAt" ? "completedAt" : "createdAt";
870
+ if (filter.fromDate) {
871
+ conditions.push(`${dateCol} >= ?`);
872
+ params.push(filter.fromDate.toISOString());
873
+ }
874
+ if (filter.toDate) {
875
+ conditions.push(`${dateCol} < ?`);
876
+ params.push(filter.toDate.toISOString());
877
+ }
878
+ if (filter.agentId) {
879
+ conditions.push("agent_id = ?");
880
+ params.push(filter.agentId);
881
+ }
882
+ if (filter.runId) {
883
+ conditions.push("run_id = ?");
884
+ params.push(filter.runId);
885
+ }
886
+ if (filter.toolCallId) {
887
+ conditions.push("tool_call_id = ?");
888
+ params.push(filter.toolCallId);
889
+ }
890
+ if (conditions.length === 0) return;
891
+ const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
892
+ await this.#db.executeQuery({
893
+ sql: `DELETE FROM ${fullTableName} WHERE ${conditions.join(" AND ")}`,
894
+ params
895
+ });
896
+ }
897
+ async getRunningCount() {
898
+ const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
899
+ const row = await this.#db.executeQuery({
900
+ sql: `SELECT COUNT(*) as count FROM ${fullTableName} WHERE status = 'running'`,
901
+ params: [],
902
+ first: true
903
+ });
904
+ return Number(row?.count ?? 0);
905
+ }
906
+ async getRunningCountByAgent(agentId) {
907
+ const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
908
+ const row = await this.#db.executeQuery({
909
+ sql: `SELECT COUNT(*) as count FROM ${fullTableName} WHERE status = 'running' AND agent_id = ?`,
910
+ params: [agentId],
911
+ first: true
912
+ });
913
+ return Number(row?.count ?? 0);
914
+ }
993
915
  };
916
+ //#endregion
917
+ //#region src/storage/domains/memory/index.ts
994
918
  function addSqliteMetadataFilter(conditions, params, metadataFilter) {
995
- if (!metadataFilter) return;
996
- for (const [key, value] of Object.entries(metadataFilter)) {
997
- const path = `$.metadata.${key}`;
998
- conditions.push(`CASE WHEN json_valid(content) THEN json_type(content, ?) IS NOT NULL ELSE 0 END`);
999
- params.push(path);
1000
- addSqliteMetadataValuePredicate(conditions, params, path, value);
1001
- }
919
+ if (!metadataFilter) return;
920
+ for (const [key, value] of Object.entries(metadataFilter)) {
921
+ const path = `$.metadata.${key}`;
922
+ conditions.push(`CASE WHEN json_valid(content) THEN json_type(content, ?) IS NOT NULL ELSE 0 END`);
923
+ params.push(path);
924
+ addSqliteMetadataValuePredicate(conditions, params, path, value);
925
+ }
1002
926
  }
1003
927
  function addSqliteMetadataValuePredicate(conditions, params, path, value) {
1004
- if (value === null) {
1005
- conditions.push(`CASE WHEN json_valid(content) THEN json_type(content, ?) = 'null' ELSE 0 END`);
1006
- params.push(path);
1007
- return;
1008
- }
1009
- if (typeof value === "string") {
1010
- conditions.push(
1011
- `CASE WHEN json_valid(content) THEN json_type(content, ?) = 'text' AND json_extract(content, ?) = ? ELSE 0 END`
1012
- );
1013
- params.push(path, path, value);
1014
- return;
1015
- }
1016
- if (typeof value === "number") {
1017
- conditions.push(
1018
- `CASE WHEN json_valid(content) THEN json_type(content, ?) IN ('integer', 'real') AND json_extract(content, ?) = ? ELSE 0 END`
1019
- );
1020
- params.push(path, path, value);
1021
- return;
1022
- }
1023
- conditions.push(
1024
- `CASE WHEN json_valid(content) THEN json_type(content, ?) = ? AND json_extract(content, ?) = ? ELSE 0 END`
1025
- );
1026
- params.push(path, value ? "true" : "false", path, value ? 1 : 0);
928
+ if (value === null) {
929
+ conditions.push(`CASE WHEN json_valid(content) THEN json_type(content, ?) = 'null' ELSE 0 END`);
930
+ params.push(path);
931
+ return;
932
+ }
933
+ if (typeof value === "string") {
934
+ conditions.push(`CASE WHEN json_valid(content) THEN json_type(content, ?) = 'text' AND json_extract(content, ?) = ? ELSE 0 END`);
935
+ params.push(path, path, value);
936
+ return;
937
+ }
938
+ if (typeof value === "number") {
939
+ conditions.push(`CASE WHEN json_valid(content) THEN json_type(content, ?) IN ('integer', 'real') AND json_extract(content, ?) = ? ELSE 0 END`);
940
+ params.push(path, path, value);
941
+ return;
942
+ }
943
+ conditions.push(`CASE WHEN json_valid(content) THEN json_type(content, ?) = ? AND json_extract(content, ?) = ? ELSE 0 END`);
944
+ params.push(path, value ? "true" : "false", path, value ? 1 : 0);
1027
945
  }
1028
946
  var MemoryStorageD1 = class extends MemoryStorage {
1029
- #db;
1030
- constructor(config) {
1031
- super();
1032
- this.#db = new D1DB(resolveD1Config(config));
1033
- }
1034
- async init() {
1035
- await this.#db.createTable({ tableName: TABLE_THREADS, schema: TABLE_SCHEMAS[TABLE_THREADS] });
1036
- await this.#db.createTable({ tableName: TABLE_MESSAGES, schema: TABLE_SCHEMAS[TABLE_MESSAGES] });
1037
- await this.#db.createTable({ tableName: TABLE_RESOURCES, schema: TABLE_SCHEMAS[TABLE_RESOURCES] });
1038
- await this.#db.alterTable({
1039
- tableName: TABLE_MESSAGES,
1040
- schema: TABLE_SCHEMAS[TABLE_MESSAGES],
1041
- ifNotExists: ["resourceId"]
1042
- });
1043
- }
1044
- async dangerouslyClearAll() {
1045
- await this.#db.clearTable({ tableName: TABLE_MESSAGES });
1046
- await this.#db.clearTable({ tableName: TABLE_THREADS });
1047
- await this.#db.clearTable({ tableName: TABLE_RESOURCES });
1048
- }
1049
- async getResourceById({ resourceId }) {
1050
- const resource = await this.#db.load({
1051
- tableName: TABLE_RESOURCES,
1052
- keys: { id: resourceId }
1053
- });
1054
- if (!resource) return null;
1055
- try {
1056
- return {
1057
- ...resource,
1058
- createdAt: ensureDate(resource.createdAt),
1059
- updatedAt: ensureDate(resource.updatedAt),
1060
- metadata: typeof resource.metadata === "string" ? JSON.parse(resource.metadata || "{}") : resource.metadata
1061
- };
1062
- } catch (error) {
1063
- const mastraError = new MastraError(
1064
- {
1065
- id: createStorageErrorId("CLOUDFLARE_D1", "GET_RESOURCE_BY_ID", "FAILED"),
1066
- domain: ErrorDomain.STORAGE,
1067
- category: ErrorCategory.THIRD_PARTY,
1068
- text: `Error processing resource ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
1069
- details: { resourceId }
1070
- },
1071
- error
1072
- );
1073
- this.logger?.error(mastraError.toString());
1074
- this.logger?.trackException(mastraError);
1075
- return null;
1076
- }
1077
- }
1078
- async saveResource({ resource }) {
1079
- const fullTableName = this.#db.getTableName(TABLE_RESOURCES);
1080
- const resourceToSave = {
1081
- id: resource.id,
1082
- workingMemory: resource.workingMemory,
1083
- metadata: resource.metadata ? JSON.stringify(resource.metadata) : null,
1084
- createdAt: resource.createdAt,
1085
- updatedAt: resource.updatedAt
1086
- };
1087
- const processedRecord = await this.#db.processRecord(resourceToSave);
1088
- const columns = Object.keys(processedRecord);
1089
- const values = Object.values(processedRecord);
1090
- const updateMap = {
1091
- workingMemory: "excluded.workingMemory",
1092
- metadata: "excluded.metadata",
1093
- createdAt: "excluded.createdAt",
1094
- updatedAt: "excluded.updatedAt"
1095
- };
1096
- const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], updateMap);
1097
- const { sql, params } = query.build();
1098
- try {
1099
- await this.#db.executeQuery({ sql, params });
1100
- return resource;
1101
- } catch (error) {
1102
- throw new MastraError(
1103
- {
1104
- id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_RESOURCE", "FAILED"),
1105
- domain: ErrorDomain.STORAGE,
1106
- category: ErrorCategory.THIRD_PARTY,
1107
- text: `Failed to save resource to ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
1108
- details: { resourceId: resource.id }
1109
- },
1110
- error
1111
- );
1112
- }
1113
- }
1114
- async updateResource({
1115
- resourceId,
1116
- workingMemory,
1117
- metadata
1118
- }) {
1119
- const existingResource = await this.getResourceById({ resourceId });
1120
- if (!existingResource) {
1121
- const newResource = {
1122
- id: resourceId,
1123
- workingMemory,
1124
- metadata: metadata || {},
1125
- createdAt: /* @__PURE__ */ new Date(),
1126
- updatedAt: /* @__PURE__ */ new Date()
1127
- };
1128
- return this.saveResource({ resource: newResource });
1129
- }
1130
- const updatedAt = /* @__PURE__ */ new Date();
1131
- const updatedResource = {
1132
- ...existingResource,
1133
- workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
1134
- metadata: {
1135
- ...existingResource.metadata,
1136
- ...metadata
1137
- },
1138
- updatedAt
1139
- };
1140
- const fullTableName = this.#db.getTableName(TABLE_RESOURCES);
1141
- const columns = ["workingMemory", "metadata", "updatedAt"];
1142
- const values = [updatedResource.workingMemory, JSON.stringify(updatedResource.metadata), updatedAt.toISOString()];
1143
- const query = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", resourceId);
1144
- const { sql, params } = query.build();
1145
- try {
1146
- await this.#db.executeQuery({ sql, params });
1147
- return updatedResource;
1148
- } catch (error) {
1149
- throw new MastraError(
1150
- {
1151
- id: createStorageErrorId("CLOUDFLARE_D1", "UPDATE_RESOURCE", "FAILED"),
1152
- domain: ErrorDomain.STORAGE,
1153
- category: ErrorCategory.THIRD_PARTY,
1154
- text: `Failed to update resource ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
1155
- details: { resourceId }
1156
- },
1157
- error
1158
- );
1159
- }
1160
- }
1161
- async getThreadById({
1162
- threadId,
1163
- resourceId
1164
- }) {
1165
- const thread = await this.#db.load({
1166
- tableName: TABLE_THREADS,
1167
- keys: { id: threadId }
1168
- });
1169
- if (!thread || resourceId !== void 0 && thread.resourceId !== resourceId) return null;
1170
- try {
1171
- return {
1172
- ...thread,
1173
- createdAt: ensureDate(thread.createdAt),
1174
- updatedAt: ensureDate(thread.updatedAt),
1175
- metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata || "{}") : thread.metadata || {}
1176
- };
1177
- } catch (error) {
1178
- const mastraError = new MastraError(
1179
- {
1180
- id: createStorageErrorId("CLOUDFLARE_D1", "GET_THREAD_BY_ID", "FAILED"),
1181
- domain: ErrorDomain.STORAGE,
1182
- category: ErrorCategory.THIRD_PARTY,
1183
- text: `Error processing thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
1184
- details: { threadId }
1185
- },
1186
- error
1187
- );
1188
- this.logger?.error(mastraError.toString());
1189
- this.logger?.trackException(mastraError);
1190
- return null;
1191
- }
1192
- }
1193
- async listThreads(args) {
1194
- const { page = 0, perPage: perPageInput, orderBy, filter } = args;
1195
- try {
1196
- this.validatePaginationInput(page, perPageInput ?? 100);
1197
- } catch (error) {
1198
- throw new MastraError(
1199
- {
1200
- id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "INVALID_PAGE"),
1201
- domain: ErrorDomain.STORAGE,
1202
- category: ErrorCategory.USER,
1203
- details: { page, ...perPageInput !== void 0 && { perPage: perPageInput } }
1204
- },
1205
- error instanceof Error ? error : new Error("Invalid pagination parameters")
1206
- );
1207
- }
1208
- const perPage = normalizePerPage(perPageInput, 100);
1209
- try {
1210
- this.validateMetadataKeys(filter?.metadata);
1211
- } catch (error) {
1212
- throw new MastraError(
1213
- {
1214
- id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "INVALID_METADATA_KEY"),
1215
- domain: ErrorDomain.STORAGE,
1216
- category: ErrorCategory.USER,
1217
- details: { metadataKeys: filter?.metadata ? Object.keys(filter.metadata).join(", ") : "" }
1218
- },
1219
- error instanceof Error ? error : new Error("Invalid metadata key")
1220
- );
1221
- }
1222
- const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1223
- const { field, direction } = this.parseOrderBy(orderBy);
1224
- const fullTableName = this.#db.getTableName(TABLE_THREADS);
1225
- const mapRowToStorageThreadType = (row) => ({
1226
- ...row,
1227
- createdAt: ensureDate(row.createdAt),
1228
- updatedAt: ensureDate(row.updatedAt),
1229
- metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata || "{}") : row.metadata || {}
1230
- });
1231
- try {
1232
- let countQuery = createSqlBuilder().count().from(fullTableName);
1233
- let selectQuery = createSqlBuilder().select("*").from(fullTableName);
1234
- if (filter?.resourceId) {
1235
- countQuery = countQuery.whereAnd("resourceId = ?", filter.resourceId);
1236
- selectQuery = selectQuery.whereAnd("resourceId = ?", filter.resourceId);
1237
- }
1238
- if (filter?.metadata && Object.keys(filter.metadata).length > 0) {
1239
- for (const [key, value] of Object.entries(filter.metadata)) {
1240
- if (value !== null && typeof value === "object") {
1241
- throw new MastraError(
1242
- {
1243
- id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "INVALID_METADATA_VALUE"),
1244
- domain: ErrorDomain.STORAGE,
1245
- category: ErrorCategory.USER,
1246
- text: `Metadata filter value for key "${key}" must be a scalar type (string, number, boolean, or null), got ${Array.isArray(value) ? "array" : "object"}`,
1247
- details: { key, valueType: Array.isArray(value) ? "array" : "object" }
1248
- },
1249
- new Error("Invalid metadata filter value type")
1250
- );
1251
- }
1252
- if (value === null) {
1253
- const condition = `json_extract(metadata, '$.${key}') IS NULL`;
1254
- countQuery = countQuery.whereAnd(condition);
1255
- selectQuery = selectQuery.whereAnd(condition);
1256
- } else {
1257
- const condition = `json_extract(metadata, '$.${key}') = ?`;
1258
- const filterValue = value;
1259
- countQuery = countQuery.whereAnd(condition, filterValue);
1260
- selectQuery = selectQuery.whereAnd(condition, filterValue);
1261
- }
1262
- }
1263
- }
1264
- const countResult = await this.#db.executeQuery(countQuery.build());
1265
- const total = Number(countResult?.[0]?.count ?? 0);
1266
- if (total === 0) {
1267
- return {
1268
- threads: [],
1269
- total: 0,
1270
- page,
1271
- perPage: perPageForResponse,
1272
- hasMore: false
1273
- };
1274
- }
1275
- const limitValue = perPageInput === false ? total : perPage;
1276
- selectQuery = selectQuery.orderBy(field, direction).limit(limitValue).offset(offset);
1277
- const results = await this.#db.executeQuery(selectQuery.build());
1278
- const threads = results.map(mapRowToStorageThreadType);
1279
- return {
1280
- threads,
1281
- total,
1282
- page,
1283
- perPage: perPageForResponse,
1284
- hasMore: perPageInput === false ? false : offset + perPage < total
1285
- };
1286
- } catch (error) {
1287
- if (error instanceof MastraError && error.category === ErrorCategory.USER) {
1288
- throw error;
1289
- }
1290
- const mastraError = new MastraError(
1291
- {
1292
- id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "FAILED"),
1293
- domain: ErrorDomain.STORAGE,
1294
- category: ErrorCategory.THIRD_PARTY,
1295
- text: `Error listing threads: ${error instanceof Error ? error.message : String(error)}`,
1296
- details: {
1297
- ...filter?.resourceId && { resourceId: filter.resourceId },
1298
- hasMetadataFilter: !!filter?.metadata
1299
- }
1300
- },
1301
- error
1302
- );
1303
- this.logger?.error(mastraError.toString());
1304
- this.logger?.trackException(mastraError);
1305
- return {
1306
- threads: [],
1307
- total: 0,
1308
- page,
1309
- perPage: perPageForResponse,
1310
- hasMore: false
1311
- };
1312
- }
1313
- }
1314
- async saveThread({ thread }) {
1315
- const fullTableName = this.#db.getTableName(TABLE_THREADS);
1316
- const threadToSave = {
1317
- id: thread.id,
1318
- resourceId: thread.resourceId,
1319
- title: thread.title,
1320
- metadata: thread.metadata ? JSON.stringify(thread.metadata) : null,
1321
- createdAt: thread.createdAt.toISOString(),
1322
- updatedAt: thread.updatedAt.toISOString()
1323
- };
1324
- const processedRecord = await this.#db.processRecord(threadToSave);
1325
- const columns = Object.keys(processedRecord);
1326
- const values = Object.values(processedRecord);
1327
- const updateMap = {
1328
- resourceId: "excluded.resourceId",
1329
- title: "excluded.title",
1330
- metadata: "excluded.metadata",
1331
- createdAt: "excluded.createdAt",
1332
- updatedAt: "excluded.updatedAt"
1333
- };
1334
- const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], updateMap);
1335
- const { sql, params } = query.build();
1336
- try {
1337
- await this.#db.executeQuery({ sql, params });
1338
- return thread;
1339
- } catch (error) {
1340
- throw new MastraError(
1341
- {
1342
- id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_THREAD", "FAILED"),
1343
- domain: ErrorDomain.STORAGE,
1344
- category: ErrorCategory.THIRD_PARTY,
1345
- text: `Failed to save thread to ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
1346
- details: { threadId: thread.id }
1347
- },
1348
- error
1349
- );
1350
- }
1351
- }
1352
- async updateThread({
1353
- id,
1354
- title,
1355
- metadata
1356
- }) {
1357
- const thread = await this.getThreadById({ threadId: id });
1358
- try {
1359
- if (!thread) {
1360
- throw new Error(`Thread ${id} not found`);
1361
- }
1362
- const fullTableName = this.#db.getTableName(TABLE_THREADS);
1363
- const mergedMetadata = {
1364
- ...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
1365
- ...metadata
1366
- };
1367
- const updatedAt = /* @__PURE__ */ new Date();
1368
- const columns = ["title", "metadata", "updatedAt"];
1369
- const values = [title, JSON.stringify(mergedMetadata), updatedAt.toISOString()];
1370
- const query = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", id);
1371
- const { sql, params } = query.build();
1372
- await this.#db.executeQuery({ sql, params });
1373
- return {
1374
- ...thread,
1375
- title,
1376
- metadata: {
1377
- ...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
1378
- ...metadata
1379
- },
1380
- updatedAt
1381
- };
1382
- } catch (error) {
1383
- throw new MastraError(
1384
- {
1385
- id: createStorageErrorId("CLOUDFLARE_D1", "UPDATE_THREAD", "FAILED"),
1386
- domain: ErrorDomain.STORAGE,
1387
- category: ErrorCategory.THIRD_PARTY,
1388
- text: `Failed to update thread ${id}: ${error instanceof Error ? error.message : String(error)}`,
1389
- details: { threadId: id }
1390
- },
1391
- error
1392
- );
1393
- }
1394
- }
1395
- async deleteThread({ threadId }) {
1396
- const fullTableName = this.#db.getTableName(TABLE_THREADS);
1397
- try {
1398
- const deleteThreadQuery = createSqlBuilder().delete(fullTableName).where("id = ?", threadId);
1399
- const { sql: threadSql, params: threadParams } = deleteThreadQuery.build();
1400
- await this.#db.executeQuery({ sql: threadSql, params: threadParams });
1401
- const messagesTableName = this.#db.getTableName(TABLE_MESSAGES);
1402
- const deleteMessagesQuery = createSqlBuilder().delete(messagesTableName).where("thread_id = ?", threadId);
1403
- const { sql: messagesSql, params: messagesParams } = deleteMessagesQuery.build();
1404
- await this.#db.executeQuery({ sql: messagesSql, params: messagesParams });
1405
- } catch (error) {
1406
- throw new MastraError(
1407
- {
1408
- id: createStorageErrorId("CLOUDFLARE_D1", "DELETE_THREAD", "FAILED"),
1409
- domain: ErrorDomain.STORAGE,
1410
- category: ErrorCategory.THIRD_PARTY,
1411
- text: `Failed to delete thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
1412
- details: { threadId }
1413
- },
1414
- error
1415
- );
1416
- }
1417
- }
1418
- async saveMessages(args) {
1419
- const { messages } = args;
1420
- if (messages.length === 0) return { messages: [] };
1421
- try {
1422
- const now = /* @__PURE__ */ new Date();
1423
- const threadId = messages[0]?.threadId;
1424
- for (const [i, message] of messages.entries()) {
1425
- if (!message.id) throw new Error(`Message at index ${i} missing id`);
1426
- if (!message.threadId) {
1427
- throw new Error(`Message at index ${i} missing threadId`);
1428
- }
1429
- if (!message.content) {
1430
- throw new Error(`Message at index ${i} missing content`);
1431
- }
1432
- if (!message.role) {
1433
- throw new Error(`Message at index ${i} missing role`);
1434
- }
1435
- if (!message.resourceId) {
1436
- throw new Error(`Message at index ${i} missing resourceId`);
1437
- }
1438
- const thread = await this.getThreadById({ threadId: message.threadId });
1439
- if (!thread) {
1440
- throw new Error(`Thread ${message.threadId} not found`);
1441
- }
1442
- }
1443
- const messagesToInsert = messages.map((message) => {
1444
- const createdAt = message.createdAt ? new Date(message.createdAt) : now;
1445
- return {
1446
- id: message.id,
1447
- thread_id: message.threadId,
1448
- content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
1449
- createdAt: createdAt.toISOString(),
1450
- role: message.role,
1451
- type: message.type || "v2",
1452
- resourceId: message.resourceId
1453
- };
1454
- });
1455
- await Promise.all([
1456
- this.#db.batchUpsert({
1457
- tableName: TABLE_MESSAGES,
1458
- records: messagesToInsert
1459
- }),
1460
- // Update thread's updatedAt timestamp
1461
- this.#db.executeQuery({
1462
- sql: `UPDATE ${this.#db.getTableName(TABLE_THREADS)} SET updatedAt = ? WHERE id = ?`,
1463
- params: [now.toISOString(), threadId]
1464
- })
1465
- ]);
1466
- this.logger.debug(`Saved ${messages.length} messages`);
1467
- const list = new MessageList().add(messages, "memory");
1468
- return { messages: list.get.all.db() };
1469
- } catch (error) {
1470
- throw new MastraError(
1471
- {
1472
- id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_MESSAGES", "FAILED"),
1473
- domain: ErrorDomain.STORAGE,
1474
- category: ErrorCategory.THIRD_PARTY,
1475
- text: `Failed to save messages: ${error instanceof Error ? error.message : String(error)}`
1476
- },
1477
- error
1478
- );
1479
- }
1480
- }
1481
- _sortMessages(messages, field, direction) {
1482
- return messages.sort((a, b) => {
1483
- const isDateField = field === "createdAt" || field === "updatedAt";
1484
- const aValue = isDateField ? new Date(a[field]).getTime() : a[field];
1485
- const bValue = isDateField ? new Date(b[field]).getTime() : b[field];
1486
- if (aValue === bValue) {
1487
- return a.id.localeCompare(b.id);
1488
- }
1489
- if (typeof aValue === "number" && typeof bValue === "number") {
1490
- return direction === "ASC" ? aValue - bValue : bValue - aValue;
1491
- }
1492
- return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
1493
- });
1494
- }
1495
- async _getIncludedMessages(include) {
1496
- if (!include || include.length === 0) return null;
1497
- const tableName = this.#db.getTableName(TABLE_MESSAGES);
1498
- const targetIds = include.map((inc) => inc.id).filter(Boolean);
1499
- if (targetIds.length === 0) return null;
1500
- const idPlaceholders = targetIds.map(() => "?").join(", ");
1501
- const targetResult = await this.#db.executeQuery({
1502
- sql: `SELECT id, thread_id, createdAt FROM ${tableName} WHERE id IN (${idPlaceholders})`,
1503
- params: targetIds
1504
- });
1505
- if (!Array.isArray(targetResult) || targetResult.length === 0) return null;
1506
- const targetMap = new Map(
1507
- targetResult.map((r) => [r.id, { threadId: r.thread_id, createdAt: r.createdAt }])
1508
- );
1509
- const MAX_UNION_TERMS = 5;
1510
- const unionQueries = [];
1511
- const unionParams = [];
1512
- const allMessages = [];
1513
- const flushBatch = async () => {
1514
- if (unionQueries.length === 0) return;
1515
- const sql = unionQueries.length === 1 ? unionQueries[0] : `${unionQueries.join(" UNION ALL ")} ORDER BY createdAt ASC, id ASC`;
1516
- const result = await this.#db.executeQuery({ sql, params: unionParams });
1517
- if (Array.isArray(result)) {
1518
- allMessages.push(...result);
1519
- }
1520
- unionQueries.length = 0;
1521
- unionParams.length = 0;
1522
- };
1523
- for (const inc of include) {
1524
- const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
1525
- const target = targetMap.get(id);
1526
- if (!target) continue;
1527
- const termsNeeded = withNextMessages > 0 ? 2 : 1;
1528
- if (unionQueries.length + termsNeeded > MAX_UNION_TERMS) {
1529
- await flushBatch();
1530
- }
1531
- unionQueries.push(`SELECT * FROM (
947
+ #db;
948
+ constructor(config) {
949
+ super();
950
+ this.#db = new D1DB(resolveD1Config(config));
951
+ }
952
+ async init() {
953
+ await this.#db.createTable({
954
+ tableName: TABLE_THREADS,
955
+ schema: TABLE_SCHEMAS[TABLE_THREADS]
956
+ });
957
+ await this.#db.createTable({
958
+ tableName: TABLE_MESSAGES,
959
+ schema: TABLE_SCHEMAS[TABLE_MESSAGES]
960
+ });
961
+ await this.#db.createTable({
962
+ tableName: TABLE_RESOURCES,
963
+ schema: TABLE_SCHEMAS[TABLE_RESOURCES]
964
+ });
965
+ await this.#db.alterTable({
966
+ tableName: TABLE_MESSAGES,
967
+ schema: TABLE_SCHEMAS[TABLE_MESSAGES],
968
+ ifNotExists: ["resourceId"]
969
+ });
970
+ }
971
+ async dangerouslyClearAll() {
972
+ await this.#db.clearTable({ tableName: TABLE_MESSAGES });
973
+ await this.#db.clearTable({ tableName: TABLE_THREADS });
974
+ await this.#db.clearTable({ tableName: TABLE_RESOURCES });
975
+ }
976
+ async getResourceById({ resourceId }) {
977
+ const resource = await this.#db.load({
978
+ tableName: TABLE_RESOURCES,
979
+ keys: { id: resourceId }
980
+ });
981
+ if (!resource) return null;
982
+ try {
983
+ return {
984
+ ...resource,
985
+ createdAt: ensureDate(resource.createdAt),
986
+ updatedAt: ensureDate(resource.updatedAt),
987
+ metadata: typeof resource.metadata === "string" ? JSON.parse(resource.metadata || "{}") : resource.metadata
988
+ };
989
+ } catch (error) {
990
+ const mastraError = new MastraError({
991
+ id: createStorageErrorId("CLOUDFLARE_D1", "GET_RESOURCE_BY_ID", "FAILED"),
992
+ domain: ErrorDomain.STORAGE,
993
+ category: ErrorCategory.THIRD_PARTY,
994
+ text: `Error processing resource ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
995
+ details: { resourceId }
996
+ }, error);
997
+ this.logger?.error(mastraError.toString());
998
+ this.logger?.trackException(mastraError);
999
+ return null;
1000
+ }
1001
+ }
1002
+ async saveResource({ resource }) {
1003
+ const fullTableName = this.#db.getTableName(TABLE_RESOURCES);
1004
+ const resourceToSave = {
1005
+ id: resource.id,
1006
+ workingMemory: resource.workingMemory,
1007
+ metadata: resource.metadata ? JSON.stringify(resource.metadata) : null,
1008
+ createdAt: resource.createdAt,
1009
+ updatedAt: resource.updatedAt
1010
+ };
1011
+ const processedRecord = await this.#db.processRecord(resourceToSave);
1012
+ const columns = Object.keys(processedRecord);
1013
+ const values = Object.values(processedRecord);
1014
+ const { sql, params } = createSqlBuilder().insert(fullTableName, columns, values, ["id"], {
1015
+ workingMemory: "excluded.workingMemory",
1016
+ metadata: "excluded.metadata",
1017
+ createdAt: "excluded.createdAt",
1018
+ updatedAt: "excluded.updatedAt"
1019
+ }).build();
1020
+ try {
1021
+ await this.#db.executeQuery({
1022
+ sql,
1023
+ params
1024
+ });
1025
+ return resource;
1026
+ } catch (error) {
1027
+ throw new MastraError({
1028
+ id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_RESOURCE", "FAILED"),
1029
+ domain: ErrorDomain.STORAGE,
1030
+ category: ErrorCategory.THIRD_PARTY,
1031
+ text: `Failed to save resource to ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
1032
+ details: { resourceId: resource.id }
1033
+ }, error);
1034
+ }
1035
+ }
1036
+ async updateResource({ resourceId, workingMemory, metadata }) {
1037
+ const existingResource = await this.getResourceById({ resourceId });
1038
+ if (!existingResource) {
1039
+ const newResource = {
1040
+ id: resourceId,
1041
+ workingMemory,
1042
+ metadata: metadata || {},
1043
+ createdAt: /* @__PURE__ */ new Date(),
1044
+ updatedAt: /* @__PURE__ */ new Date()
1045
+ };
1046
+ return this.saveResource({ resource: newResource });
1047
+ }
1048
+ const updatedAt = /* @__PURE__ */ new Date();
1049
+ const updatedResource = {
1050
+ ...existingResource,
1051
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
1052
+ metadata: {
1053
+ ...existingResource.metadata,
1054
+ ...metadata
1055
+ },
1056
+ updatedAt
1057
+ };
1058
+ const fullTableName = this.#db.getTableName(TABLE_RESOURCES);
1059
+ const columns = [
1060
+ "workingMemory",
1061
+ "metadata",
1062
+ "updatedAt"
1063
+ ];
1064
+ const values = [
1065
+ updatedResource.workingMemory,
1066
+ JSON.stringify(updatedResource.metadata),
1067
+ updatedAt.toISOString()
1068
+ ];
1069
+ const { sql, params } = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", resourceId).build();
1070
+ try {
1071
+ await this.#db.executeQuery({
1072
+ sql,
1073
+ params
1074
+ });
1075
+ return updatedResource;
1076
+ } catch (error) {
1077
+ throw new MastraError({
1078
+ id: createStorageErrorId("CLOUDFLARE_D1", "UPDATE_RESOURCE", "FAILED"),
1079
+ domain: ErrorDomain.STORAGE,
1080
+ category: ErrorCategory.THIRD_PARTY,
1081
+ text: `Failed to update resource ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
1082
+ details: { resourceId }
1083
+ }, error);
1084
+ }
1085
+ }
1086
+ async getThreadById({ threadId, resourceId }) {
1087
+ const thread = await this.#db.load({
1088
+ tableName: TABLE_THREADS,
1089
+ keys: { id: threadId }
1090
+ });
1091
+ if (!thread || resourceId !== void 0 && thread.resourceId !== resourceId) return null;
1092
+ try {
1093
+ return {
1094
+ ...thread,
1095
+ createdAt: ensureDate(thread.createdAt),
1096
+ updatedAt: ensureDate(thread.updatedAt),
1097
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata || "{}") : thread.metadata || {}
1098
+ };
1099
+ } catch (error) {
1100
+ const mastraError = new MastraError({
1101
+ id: createStorageErrorId("CLOUDFLARE_D1", "GET_THREAD_BY_ID", "FAILED"),
1102
+ domain: ErrorDomain.STORAGE,
1103
+ category: ErrorCategory.THIRD_PARTY,
1104
+ text: `Error processing thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
1105
+ details: { threadId }
1106
+ }, error);
1107
+ this.logger?.error(mastraError.toString());
1108
+ this.logger?.trackException(mastraError);
1109
+ return null;
1110
+ }
1111
+ }
1112
+ async listThreads(args) {
1113
+ const { page = 0, perPage: perPageInput, orderBy, filter } = args;
1114
+ try {
1115
+ this.validatePaginationInput(page, perPageInput ?? 100);
1116
+ } catch (error) {
1117
+ throw new MastraError({
1118
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "INVALID_PAGE"),
1119
+ domain: ErrorDomain.STORAGE,
1120
+ category: ErrorCategory.USER,
1121
+ details: {
1122
+ page,
1123
+ ...perPageInput !== void 0 && { perPage: perPageInput }
1124
+ }
1125
+ }, error instanceof Error ? error : /* @__PURE__ */ new Error("Invalid pagination parameters"));
1126
+ }
1127
+ const perPage = normalizePerPage(perPageInput, 100);
1128
+ try {
1129
+ this.validateMetadataKeys(filter?.metadata);
1130
+ } catch (error) {
1131
+ throw new MastraError({
1132
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "INVALID_METADATA_KEY"),
1133
+ domain: ErrorDomain.STORAGE,
1134
+ category: ErrorCategory.USER,
1135
+ details: { metadataKeys: filter?.metadata ? Object.keys(filter.metadata).join(", ") : "" }
1136
+ }, error instanceof Error ? error : /* @__PURE__ */ new Error("Invalid metadata key"));
1137
+ }
1138
+ const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1139
+ const { field, direction } = this.parseOrderBy(orderBy);
1140
+ const fullTableName = this.#db.getTableName(TABLE_THREADS);
1141
+ const mapRowToStorageThreadType = (row) => ({
1142
+ ...row,
1143
+ createdAt: ensureDate(row.createdAt),
1144
+ updatedAt: ensureDate(row.updatedAt),
1145
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata || "{}") : row.metadata || {}
1146
+ });
1147
+ try {
1148
+ let countQuery = createSqlBuilder().count().from(fullTableName);
1149
+ let selectQuery = createSqlBuilder().select("*").from(fullTableName);
1150
+ if (filter?.resourceId) {
1151
+ countQuery = countQuery.whereAnd("resourceId = ?", filter.resourceId);
1152
+ selectQuery = selectQuery.whereAnd("resourceId = ?", filter.resourceId);
1153
+ }
1154
+ if (filter?.metadata && Object.keys(filter.metadata).length > 0) for (const [key, value] of Object.entries(filter.metadata)) {
1155
+ if (value !== null && typeof value === "object") throw new MastraError({
1156
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "INVALID_METADATA_VALUE"),
1157
+ domain: ErrorDomain.STORAGE,
1158
+ category: ErrorCategory.USER,
1159
+ text: `Metadata filter value for key "${key}" must be a scalar type (string, number, boolean, or null), got ${Array.isArray(value) ? "array" : "object"}`,
1160
+ details: {
1161
+ key,
1162
+ valueType: Array.isArray(value) ? "array" : "object"
1163
+ }
1164
+ }, /* @__PURE__ */ new Error("Invalid metadata filter value type"));
1165
+ if (value === null) {
1166
+ const condition = `json_extract(metadata, '$.${key}') IS NULL`;
1167
+ countQuery = countQuery.whereAnd(condition);
1168
+ selectQuery = selectQuery.whereAnd(condition);
1169
+ } else {
1170
+ const condition = `json_extract(metadata, '$.${key}') = ?`;
1171
+ const filterValue = value;
1172
+ countQuery = countQuery.whereAnd(condition, filterValue);
1173
+ selectQuery = selectQuery.whereAnd(condition, filterValue);
1174
+ }
1175
+ }
1176
+ const countResult = await this.#db.executeQuery(countQuery.build());
1177
+ const total = Number(countResult?.[0]?.count ?? 0);
1178
+ if (total === 0) return {
1179
+ threads: [],
1180
+ total: 0,
1181
+ page,
1182
+ perPage: perPageForResponse,
1183
+ hasMore: false
1184
+ };
1185
+ const limitValue = perPageInput === false ? total : perPage;
1186
+ selectQuery = selectQuery.orderBy(field, direction).limit(limitValue).offset(offset);
1187
+ return {
1188
+ threads: (await this.#db.executeQuery(selectQuery.build())).map(mapRowToStorageThreadType),
1189
+ total,
1190
+ page,
1191
+ perPage: perPageForResponse,
1192
+ hasMore: perPageInput === false ? false : offset + perPage < total
1193
+ };
1194
+ } catch (error) {
1195
+ if (error instanceof MastraError && error.category === ErrorCategory.USER) throw error;
1196
+ const mastraError = new MastraError({
1197
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "FAILED"),
1198
+ domain: ErrorDomain.STORAGE,
1199
+ category: ErrorCategory.THIRD_PARTY,
1200
+ text: `Error listing threads: ${error instanceof Error ? error.message : String(error)}`,
1201
+ details: {
1202
+ ...filter?.resourceId && { resourceId: filter.resourceId },
1203
+ hasMetadataFilter: !!filter?.metadata
1204
+ }
1205
+ }, error);
1206
+ this.logger?.error(mastraError.toString());
1207
+ this.logger?.trackException(mastraError);
1208
+ return {
1209
+ threads: [],
1210
+ total: 0,
1211
+ page,
1212
+ perPage: perPageForResponse,
1213
+ hasMore: false
1214
+ };
1215
+ }
1216
+ }
1217
+ async saveThread({ thread }) {
1218
+ const fullTableName = this.#db.getTableName(TABLE_THREADS);
1219
+ const threadToSave = {
1220
+ id: thread.id,
1221
+ resourceId: thread.resourceId,
1222
+ title: thread.title,
1223
+ metadata: thread.metadata ? JSON.stringify(thread.metadata) : null,
1224
+ createdAt: thread.createdAt.toISOString(),
1225
+ updatedAt: thread.updatedAt.toISOString()
1226
+ };
1227
+ const processedRecord = await this.#db.processRecord(threadToSave);
1228
+ const columns = Object.keys(processedRecord);
1229
+ const values = Object.values(processedRecord);
1230
+ const { sql, params } = createSqlBuilder().insert(fullTableName, columns, values, ["id"], {
1231
+ resourceId: "excluded.resourceId",
1232
+ title: "excluded.title",
1233
+ metadata: "excluded.metadata",
1234
+ createdAt: "excluded.createdAt",
1235
+ updatedAt: "excluded.updatedAt"
1236
+ }).build();
1237
+ try {
1238
+ await this.#db.executeQuery({
1239
+ sql,
1240
+ params
1241
+ });
1242
+ return thread;
1243
+ } catch (error) {
1244
+ throw new MastraError({
1245
+ id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_THREAD", "FAILED"),
1246
+ domain: ErrorDomain.STORAGE,
1247
+ category: ErrorCategory.THIRD_PARTY,
1248
+ text: `Failed to save thread to ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
1249
+ details: { threadId: thread.id }
1250
+ }, error);
1251
+ }
1252
+ }
1253
+ async updateThread({ id, title, metadata }) {
1254
+ const thread = await this.getThreadById({ threadId: id });
1255
+ try {
1256
+ if (!thread) throw new Error(`Thread ${id} not found`);
1257
+ const fullTableName = this.#db.getTableName(TABLE_THREADS);
1258
+ const mergedMetadata = {
1259
+ ...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
1260
+ ...metadata
1261
+ };
1262
+ const updatedAt = /* @__PURE__ */ new Date();
1263
+ const columns = [
1264
+ "title",
1265
+ "metadata",
1266
+ "updatedAt"
1267
+ ];
1268
+ const values = [
1269
+ title,
1270
+ JSON.stringify(mergedMetadata),
1271
+ updatedAt.toISOString()
1272
+ ];
1273
+ const { sql, params } = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", id).build();
1274
+ await this.#db.executeQuery({
1275
+ sql,
1276
+ params
1277
+ });
1278
+ return {
1279
+ ...thread,
1280
+ title,
1281
+ metadata: {
1282
+ ...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
1283
+ ...metadata
1284
+ },
1285
+ updatedAt
1286
+ };
1287
+ } catch (error) {
1288
+ throw new MastraError({
1289
+ id: createStorageErrorId("CLOUDFLARE_D1", "UPDATE_THREAD", "FAILED"),
1290
+ domain: ErrorDomain.STORAGE,
1291
+ category: ErrorCategory.THIRD_PARTY,
1292
+ text: `Failed to update thread ${id}: ${error instanceof Error ? error.message : String(error)}`,
1293
+ details: { threadId: id }
1294
+ }, error);
1295
+ }
1296
+ }
1297
+ async deleteThread({ threadId }) {
1298
+ const fullTableName = this.#db.getTableName(TABLE_THREADS);
1299
+ try {
1300
+ const { sql: threadSql, params: threadParams } = createSqlBuilder().delete(fullTableName).where("id = ?", threadId).build();
1301
+ await this.#db.executeQuery({
1302
+ sql: threadSql,
1303
+ params: threadParams
1304
+ });
1305
+ const messagesTableName = this.#db.getTableName(TABLE_MESSAGES);
1306
+ const { sql: messagesSql, params: messagesParams } = createSqlBuilder().delete(messagesTableName).where("thread_id = ?", threadId).build();
1307
+ await this.#db.executeQuery({
1308
+ sql: messagesSql,
1309
+ params: messagesParams
1310
+ });
1311
+ } catch (error) {
1312
+ throw new MastraError({
1313
+ id: createStorageErrorId("CLOUDFLARE_D1", "DELETE_THREAD", "FAILED"),
1314
+ domain: ErrorDomain.STORAGE,
1315
+ category: ErrorCategory.THIRD_PARTY,
1316
+ text: `Failed to delete thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
1317
+ details: { threadId }
1318
+ }, error);
1319
+ }
1320
+ }
1321
+ async saveMessages(args) {
1322
+ const { messages } = args;
1323
+ if (messages.length === 0) return { messages: [] };
1324
+ try {
1325
+ const now = /* @__PURE__ */ new Date();
1326
+ const threadId = messages[0]?.threadId;
1327
+ for (const [i, message] of messages.entries()) {
1328
+ if (!message.id) throw new Error(`Message at index ${i} missing id`);
1329
+ if (!message.threadId) throw new Error(`Message at index ${i} missing threadId`);
1330
+ if (!message.content) throw new Error(`Message at index ${i} missing content`);
1331
+ if (!message.role) throw new Error(`Message at index ${i} missing role`);
1332
+ if (!message.resourceId) throw new Error(`Message at index ${i} missing resourceId`);
1333
+ if (!await this.getThreadById({ threadId: message.threadId })) throw new Error(`Thread ${message.threadId} not found`);
1334
+ }
1335
+ const messagesToInsert = messages.map((message) => {
1336
+ const createdAt = message.createdAt ? new Date(message.createdAt) : now;
1337
+ return {
1338
+ id: message.id,
1339
+ thread_id: message.threadId,
1340
+ content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
1341
+ createdAt: createdAt.toISOString(),
1342
+ role: message.role,
1343
+ type: message.type || "v2",
1344
+ resourceId: message.resourceId
1345
+ };
1346
+ });
1347
+ await Promise.all([this.#db.batchUpsert({
1348
+ tableName: TABLE_MESSAGES,
1349
+ records: messagesToInsert
1350
+ }), this.#db.executeQuery({
1351
+ sql: `UPDATE ${this.#db.getTableName(TABLE_THREADS)} SET updatedAt = ? WHERE id = ?`,
1352
+ params: [now.toISOString(), threadId]
1353
+ })]);
1354
+ this.logger.debug(`Saved ${messages.length} messages`);
1355
+ return { messages: new MessageList().add(messages, "memory").get.all.db() };
1356
+ } catch (error) {
1357
+ throw new MastraError({
1358
+ id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_MESSAGES", "FAILED"),
1359
+ domain: ErrorDomain.STORAGE,
1360
+ category: ErrorCategory.THIRD_PARTY,
1361
+ text: `Failed to save messages: ${error instanceof Error ? error.message : String(error)}`
1362
+ }, error);
1363
+ }
1364
+ }
1365
+ _sortMessages(messages, field, direction) {
1366
+ return messages.sort((a, b) => {
1367
+ const isDateField = field === "createdAt" || field === "updatedAt";
1368
+ const aValue = isDateField ? new Date(a[field]).getTime() : a[field];
1369
+ const bValue = isDateField ? new Date(b[field]).getTime() : b[field];
1370
+ if (aValue === bValue) return a.id.localeCompare(b.id);
1371
+ if (typeof aValue === "number" && typeof bValue === "number") return direction === "ASC" ? aValue - bValue : bValue - aValue;
1372
+ return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
1373
+ });
1374
+ }
1375
+ async _getIncludedMessages(include) {
1376
+ if (!include || include.length === 0) return null;
1377
+ const tableName = this.#db.getTableName(TABLE_MESSAGES);
1378
+ const targetIds = include.map((inc) => inc.id).filter(Boolean);
1379
+ if (targetIds.length === 0) return null;
1380
+ const idPlaceholders = targetIds.map(() => "?").join(", ");
1381
+ const targetResult = await this.#db.executeQuery({
1382
+ sql: `SELECT id, thread_id, createdAt FROM ${tableName} WHERE id IN (${idPlaceholders})`,
1383
+ params: targetIds
1384
+ });
1385
+ if (!Array.isArray(targetResult) || targetResult.length === 0) return null;
1386
+ const targetMap = new Map(targetResult.map((r) => [r.id, {
1387
+ threadId: r.thread_id,
1388
+ createdAt: r.createdAt
1389
+ }]));
1390
+ const MAX_UNION_TERMS = 5;
1391
+ const unionQueries = [];
1392
+ const unionParams = [];
1393
+ const allMessages = [];
1394
+ const flushBatch = async () => {
1395
+ if (unionQueries.length === 0) return;
1396
+ const sql = unionQueries.length === 1 ? unionQueries[0] : `${unionQueries.join(" UNION ALL ")} ORDER BY createdAt ASC, id ASC`;
1397
+ const result = await this.#db.executeQuery({
1398
+ sql,
1399
+ params: unionParams
1400
+ });
1401
+ if (Array.isArray(result)) allMessages.push(...result);
1402
+ unionQueries.length = 0;
1403
+ unionParams.length = 0;
1404
+ };
1405
+ for (const inc of include) {
1406
+ const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
1407
+ const target = targetMap.get(id);
1408
+ if (!target) continue;
1409
+ const termsNeeded = withNextMessages > 0 ? 2 : 1;
1410
+ if (unionQueries.length + termsNeeded > MAX_UNION_TERMS) await flushBatch();
1411
+ unionQueries.push(`SELECT * FROM (
1532
1412
  SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId
1533
1413
  FROM ${tableName}
1534
1414
  WHERE thread_id = ?
@@ -1536,9 +1416,9 @@ var MemoryStorageD1 = class extends MemoryStorage {
1536
1416
  ORDER BY createdAt DESC, id DESC
1537
1417
  LIMIT ?
1538
1418
  )`);
1539
- unionParams.push(target.threadId, target.createdAt, withPreviousMessages + 1);
1540
- if (withNextMessages > 0) {
1541
- unionQueries.push(`SELECT * FROM (
1419
+ unionParams.push(target.threadId, target.createdAt, withPreviousMessages + 1);
1420
+ if (withNextMessages > 0) {
1421
+ unionQueries.push(`SELECT * FROM (
1542
1422
  SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId
1543
1423
  FROM ${tableName}
1544
1424
  WHERE thread_id = ?
@@ -1546,1054 +1426,1013 @@ var MemoryStorageD1 = class extends MemoryStorage {
1546
1426
  ORDER BY createdAt ASC, id ASC
1547
1427
  LIMIT ?
1548
1428
  )`);
1549
- unionParams.push(target.threadId, target.createdAt, withNextMessages);
1550
- }
1551
- }
1552
- await flushBatch();
1553
- const seen = /* @__PURE__ */ new Set();
1554
- const processedMessages = allMessages.filter((message) => {
1555
- const id = message.id;
1556
- if (seen.has(id)) return false;
1557
- seen.add(id);
1558
- return true;
1559
- }).map((message) => {
1560
- const processedMsg = {};
1561
- for (const [key, value] of Object.entries(message)) {
1562
- if (key === `type` && value === `v2`) continue;
1563
- processedMsg[key] = deserializeValue(value);
1564
- }
1565
- return processedMsg;
1566
- });
1567
- return processedMessages;
1568
- }
1569
- async listMessagesById({ messageIds }) {
1570
- if (messageIds.length === 0) return { messages: [] };
1571
- const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
1572
- const messages = [];
1573
- try {
1574
- const query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId", "resourceId"]).from(fullTableName).where(`id in (${messageIds.map(() => "?").join(",")})`, ...messageIds);
1575
- query.orderBy("createdAt", "DESC");
1576
- const { sql, params } = query.build();
1577
- const result = await this.#db.executeQuery({ sql, params });
1578
- if (Array.isArray(result)) messages.push(...result);
1579
- const processedMessages = messages.map((message) => {
1580
- const processedMsg = {};
1581
- for (const [key, value] of Object.entries(message)) {
1582
- if (key === `type` && value === `v2`) continue;
1583
- processedMsg[key] = deserializeValue(value);
1584
- }
1585
- return processedMsg;
1586
- });
1587
- this.logger.debug(`Retrieved ${messages.length} messages`);
1588
- const list = new MessageList().add(processedMessages, "memory");
1589
- return { messages: list.get.all.db() };
1590
- } catch (error) {
1591
- const mastraError = new MastraError(
1592
- {
1593
- id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES_BY_ID", "FAILED"),
1594
- domain: ErrorDomain.STORAGE,
1595
- category: ErrorCategory.THIRD_PARTY,
1596
- text: `Failed to retrieve messages by ID: ${error instanceof Error ? error.message : String(error)}`,
1597
- details: { messageIds: JSON.stringify(messageIds) }
1598
- },
1599
- error
1600
- );
1601
- this.logger?.error(mastraError.toString());
1602
- this.logger?.trackException(mastraError);
1603
- throw mastraError;
1604
- }
1605
- }
1606
- async listMessages(args) {
1607
- const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
1608
- const threadIds = Array.isArray(threadId) ? threadId : [threadId];
1609
- if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
1610
- throw new MastraError(
1611
- {
1612
- id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "INVALID_THREAD_ID"),
1613
- domain: ErrorDomain.STORAGE,
1614
- category: ErrorCategory.THIRD_PARTY,
1615
- details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
1616
- },
1617
- new Error("threadId must be a non-empty string or array of non-empty strings")
1618
- );
1619
- }
1620
- if (page < 0) {
1621
- throw new MastraError(
1622
- {
1623
- id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "INVALID_PAGE"),
1624
- domain: ErrorDomain.STORAGE,
1625
- category: ErrorCategory.USER,
1626
- details: { page }
1627
- },
1628
- new Error("page must be >= 0")
1629
- );
1630
- }
1631
- const perPage = normalizePerPage(perPageInput, 40);
1632
- const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1633
- const metadataFilter = validateStorageMetadataFilter(filter?.metadata);
1634
- try {
1635
- const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
1636
- let query = `
1429
+ unionParams.push(target.threadId, target.createdAt, withNextMessages);
1430
+ }
1431
+ }
1432
+ await flushBatch();
1433
+ const seen = /* @__PURE__ */ new Set();
1434
+ return allMessages.filter((message) => {
1435
+ const id = message.id;
1436
+ if (seen.has(id)) return false;
1437
+ seen.add(id);
1438
+ return true;
1439
+ }).map((message) => {
1440
+ const processedMsg = {};
1441
+ for (const [key, value] of Object.entries(message)) {
1442
+ if (key === `type` && value === `v2`) continue;
1443
+ processedMsg[key] = deserializeValue(value);
1444
+ }
1445
+ return processedMsg;
1446
+ });
1447
+ }
1448
+ async listMessagesById({ messageIds }) {
1449
+ if (messageIds.length === 0) return { messages: [] };
1450
+ const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
1451
+ const messages = [];
1452
+ try {
1453
+ const query = createSqlBuilder().select([
1454
+ "id",
1455
+ "content",
1456
+ "role",
1457
+ "type",
1458
+ "createdAt",
1459
+ "thread_id AS threadId",
1460
+ "resourceId"
1461
+ ]).from(fullTableName).where(`id in (${messageIds.map(() => "?").join(",")})`, ...messageIds);
1462
+ query.orderBy("createdAt", "DESC");
1463
+ const { sql, params } = query.build();
1464
+ const result = await this.#db.executeQuery({
1465
+ sql,
1466
+ params
1467
+ });
1468
+ if (Array.isArray(result)) messages.push(...result);
1469
+ const processedMessages = messages.map((message) => {
1470
+ const processedMsg = {};
1471
+ for (const [key, value] of Object.entries(message)) {
1472
+ if (key === `type` && value === `v2`) continue;
1473
+ processedMsg[key] = deserializeValue(value);
1474
+ }
1475
+ return processedMsg;
1476
+ });
1477
+ this.logger.debug(`Retrieved ${messages.length} messages`);
1478
+ return { messages: new MessageList().add(processedMessages, "memory").get.all.db() };
1479
+ } catch (error) {
1480
+ const mastraError = new MastraError({
1481
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES_BY_ID", "FAILED"),
1482
+ domain: ErrorDomain.STORAGE,
1483
+ category: ErrorCategory.THIRD_PARTY,
1484
+ text: `Failed to retrieve messages by ID: ${error instanceof Error ? error.message : String(error)}`,
1485
+ details: { messageIds: JSON.stringify(messageIds) }
1486
+ }, error);
1487
+ this.logger?.error(mastraError.toString());
1488
+ this.logger?.trackException(mastraError);
1489
+ throw mastraError;
1490
+ }
1491
+ }
1492
+ async listMessages(args) {
1493
+ const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
1494
+ const threadIds = Array.isArray(threadId) ? threadId : [threadId];
1495
+ if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) throw new MastraError({
1496
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "INVALID_THREAD_ID"),
1497
+ domain: ErrorDomain.STORAGE,
1498
+ category: ErrorCategory.THIRD_PARTY,
1499
+ details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
1500
+ }, /* @__PURE__ */ new Error("threadId must be a non-empty string or array of non-empty strings"));
1501
+ if (page < 0) throw new MastraError({
1502
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "INVALID_PAGE"),
1503
+ domain: ErrorDomain.STORAGE,
1504
+ category: ErrorCategory.USER,
1505
+ details: { page }
1506
+ }, /* @__PURE__ */ new Error("page must be >= 0"));
1507
+ const perPage = normalizePerPage(perPageInput, 40);
1508
+ const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1509
+ const metadataFilter = validateStorageMetadataFilter(filter?.metadata);
1510
+ try {
1511
+ const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
1512
+ const threadPlaceholders = threadIds.map(() => "?").join(", ");
1513
+ let query = `
1637
1514
  SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId
1638
1515
  FROM ${fullTableName}
1639
- WHERE thread_id = ?
1516
+ WHERE thread_id IN (${threadPlaceholders})
1640
1517
  `;
1641
- const queryParams = [threadId];
1642
- if (resourceId) {
1643
- query += ` AND resourceId = ?`;
1644
- queryParams.push(resourceId);
1645
- }
1646
- const dateRange = filter?.dateRange;
1647
- if (dateRange?.start) {
1648
- const startDate = dateRange.start instanceof Date ? serializeDate(dateRange.start) : serializeDate(new Date(dateRange.start));
1649
- const startOp = dateRange.startExclusive ? ">" : ">=";
1650
- query += ` AND createdAt ${startOp} ?`;
1651
- queryParams.push(startDate);
1652
- }
1653
- if (dateRange?.end) {
1654
- const endDate = dateRange.end instanceof Date ? serializeDate(dateRange.end) : serializeDate(new Date(dateRange.end));
1655
- const endOp = dateRange.endExclusive ? "<" : "<=";
1656
- query += ` AND createdAt ${endOp} ?`;
1657
- queryParams.push(endDate);
1658
- }
1659
- const metadataConditions = [];
1660
- addSqliteMetadataFilter(metadataConditions, queryParams, metadataFilter);
1661
- if (metadataConditions.length > 0) {
1662
- query += ` AND ${metadataConditions.join(" AND ")}`;
1663
- }
1664
- const { field, direction } = this.parseOrderBy(orderBy, "ASC");
1665
- if (perPage === 0 && (!include || include.length === 0)) {
1666
- return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
1667
- }
1668
- if (perPage === 0 && include && include.length > 0) {
1669
- const includeResult = await this._getIncludedMessages(include);
1670
- if (!Array.isArray(includeResult) || includeResult.length === 0) {
1671
- return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
1672
- }
1673
- const list2 = new MessageList().add(includeResult, "memory");
1674
- return {
1675
- messages: this._sortMessages(list2.get.all.db(), field, direction),
1676
- total: 0,
1677
- page,
1678
- perPage: perPageForResponse,
1679
- hasMore: false
1680
- };
1681
- }
1682
- query += ` ORDER BY "${field}" ${direction}`;
1683
- if (perPage !== Number.MAX_SAFE_INTEGER) {
1684
- query += ` LIMIT ? OFFSET ?`;
1685
- queryParams.push(perPage, offset);
1686
- }
1687
- const results = await this.#db.executeQuery({ sql: query, params: queryParams });
1688
- const paginatedMessages = (isArrayOfRecords(results) ? results : []).map((message) => {
1689
- const processedMsg = {};
1690
- for (const [key, value] of Object.entries(message)) {
1691
- if (key === `type` && value === `v2`) continue;
1692
- processedMsg[key] = deserializeValue(value);
1693
- }
1694
- return processedMsg;
1695
- });
1696
- const paginatedCount = paginatedMessages.length;
1697
- let countQuery = `SELECT count() as count FROM ${fullTableName} WHERE thread_id = ?`;
1698
- const countParams = [threadId];
1699
- if (resourceId) {
1700
- countQuery += ` AND resourceId = ?`;
1701
- countParams.push(resourceId);
1702
- }
1703
- if (dateRange?.start) {
1704
- const startDate = dateRange.start instanceof Date ? serializeDate(dateRange.start) : serializeDate(new Date(dateRange.start));
1705
- const startOp = dateRange.startExclusive ? ">" : ">=";
1706
- countQuery += ` AND createdAt ${startOp} ?`;
1707
- countParams.push(startDate);
1708
- }
1709
- if (dateRange?.end) {
1710
- const endDate = dateRange.end instanceof Date ? serializeDate(dateRange.end) : serializeDate(new Date(dateRange.end));
1711
- const endOp = dateRange.endExclusive ? "<" : "<=";
1712
- countQuery += ` AND createdAt ${endOp} ?`;
1713
- countParams.push(endDate);
1714
- }
1715
- const countMetadataConditions = [];
1716
- addSqliteMetadataFilter(countMetadataConditions, countParams, metadataFilter);
1717
- if (countMetadataConditions.length > 0) {
1718
- countQuery += ` AND ${countMetadataConditions.join(" AND ")}`;
1719
- }
1720
- const countResult = await this.#db.executeQuery({ sql: countQuery, params: countParams });
1721
- const total = Number(countResult[0]?.count ?? 0);
1722
- if (total === 0 && paginatedCount === 0 && (!include || include.length === 0)) {
1723
- return {
1724
- messages: [],
1725
- total: 0,
1726
- page,
1727
- perPage: perPageForResponse,
1728
- hasMore: false
1729
- };
1730
- }
1731
- const messageIds = new Set(paginatedMessages.map((m) => m.id));
1732
- let includeMessages = [];
1733
- if (include && include.length > 0) {
1734
- const includeResult = await this._getIncludedMessages(include);
1735
- if (Array.isArray(includeResult)) {
1736
- includeMessages = includeResult;
1737
- for (const includeMsg of includeMessages) {
1738
- if (!messageIds.has(includeMsg.id)) {
1739
- paginatedMessages.push(includeMsg);
1740
- messageIds.add(includeMsg.id);
1741
- }
1742
- }
1743
- }
1744
- }
1745
- const list = new MessageList().add(paginatedMessages, "memory");
1746
- const finalMessages = this._sortMessages(list.get.all.db(), field, direction);
1747
- const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
1748
- const hasMore = perPageInput !== false && (metadataFilter || returnedThreadMessageIds.size < total) && offset + paginatedCount < total;
1749
- return {
1750
- messages: finalMessages,
1751
- total,
1752
- page,
1753
- perPage: perPageForResponse,
1754
- hasMore
1755
- };
1756
- } catch (error) {
1757
- const mastraError = new MastraError(
1758
- {
1759
- id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "FAILED"),
1760
- domain: ErrorDomain.STORAGE,
1761
- category: ErrorCategory.THIRD_PARTY,
1762
- text: `Failed to list messages for thread ${Array.isArray(threadId) ? threadId.join(",") : threadId}: ${error instanceof Error ? error.message : String(error)}`,
1763
- details: {
1764
- threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
1765
- resourceId: resourceId ?? ""
1766
- }
1767
- },
1768
- error
1769
- );
1770
- this.logger?.error?.(mastraError.toString());
1771
- this.logger?.trackException?.(mastraError);
1772
- return {
1773
- messages: [],
1774
- total: 0,
1775
- page,
1776
- perPage: perPageForResponse,
1777
- hasMore: false
1778
- };
1779
- }
1780
- }
1781
- async updateMessages(args) {
1782
- const { messages } = args;
1783
- this.logger.debug("Updating messages", { count: messages.length });
1784
- if (!messages.length) {
1785
- return [];
1786
- }
1787
- const messageIds = messages.map((m) => m.id);
1788
- const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
1789
- const threadsTableName = this.#db.getTableName(TABLE_THREADS);
1790
- try {
1791
- const placeholders = messageIds.map(() => "?").join(",");
1792
- const selectQuery = `SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId FROM ${fullTableName} WHERE id IN (${placeholders})`;
1793
- const existingMessages = await this.#db.executeQuery({ sql: selectQuery, params: messageIds });
1794
- if (existingMessages.length === 0) {
1795
- return [];
1796
- }
1797
- const parsedExistingMessages = existingMessages.map((msg) => {
1798
- if (typeof msg.content === "string") {
1799
- try {
1800
- msg.content = JSON.parse(msg.content);
1801
- } catch {
1802
- }
1803
- }
1804
- return msg;
1805
- });
1806
- const threadIdsToUpdate = /* @__PURE__ */ new Set();
1807
- const updateQueries = [];
1808
- for (const existingMessage of parsedExistingMessages) {
1809
- const updatePayload = messages.find((m) => m.id === existingMessage.id);
1810
- if (!updatePayload) continue;
1811
- const { id, ...fieldsToUpdate } = updatePayload;
1812
- if (Object.keys(fieldsToUpdate).length === 0) continue;
1813
- threadIdsToUpdate.add(existingMessage.threadId);
1814
- if ("threadId" in updatePayload && updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
1815
- threadIdsToUpdate.add(updatePayload.threadId);
1816
- }
1817
- const setClauses = [];
1818
- const values = [];
1819
- const updatableFields = { ...fieldsToUpdate };
1820
- if (updatableFields.content) {
1821
- const existingContent = existingMessage.content || {};
1822
- const newContent = {
1823
- ...existingContent,
1824
- ...updatableFields.content,
1825
- // Deep merge metadata if it exists on both
1826
- ...existingContent?.metadata && updatableFields.content.metadata ? {
1827
- metadata: {
1828
- ...existingContent.metadata,
1829
- ...updatableFields.content.metadata
1830
- }
1831
- } : {}
1832
- };
1833
- setClauses.push(`content = ?`);
1834
- values.push(JSON.stringify(newContent));
1835
- delete updatableFields.content;
1836
- }
1837
- for (const key in updatableFields) {
1838
- if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
1839
- const dbColumn = key === "threadId" ? "thread_id" : key;
1840
- setClauses.push(`${dbColumn} = ?`);
1841
- values.push(updatableFields[key]);
1842
- }
1843
- }
1844
- if (setClauses.length > 0) {
1845
- values.push(id);
1846
- const updateQuery = `UPDATE ${fullTableName} SET ${setClauses.join(", ")} WHERE id = ?`;
1847
- updateQueries.push({ sql: updateQuery, params: values });
1848
- }
1849
- }
1850
- for (const query of updateQueries) {
1851
- await this.#db.executeQuery(query);
1852
- }
1853
- if (threadIdsToUpdate.size > 0) {
1854
- const threadPlaceholders = Array.from(threadIdsToUpdate).map(() => "?").join(",");
1855
- const threadUpdateQuery = `UPDATE ${threadsTableName} SET updatedAt = ? WHERE id IN (${threadPlaceholders})`;
1856
- const threadUpdateParams = [(/* @__PURE__ */ new Date()).toISOString(), ...Array.from(threadIdsToUpdate)];
1857
- await this.#db.executeQuery({ sql: threadUpdateQuery, params: threadUpdateParams });
1858
- }
1859
- const updatedMessages = await this.#db.executeQuery({ sql: selectQuery, params: messageIds });
1860
- return updatedMessages.map((message) => {
1861
- if (typeof message.content === "string") {
1862
- try {
1863
- message.content = JSON.parse(message.content);
1864
- } catch {
1865
- }
1866
- }
1867
- return message;
1868
- });
1869
- } catch (error) {
1870
- throw new MastraError(
1871
- {
1872
- id: createStorageErrorId("CLOUDFLARE_D1", "UPDATE_MESSAGES", "FAILED"),
1873
- domain: ErrorDomain.STORAGE,
1874
- category: ErrorCategory.THIRD_PARTY,
1875
- details: { count: messages.length }
1876
- },
1877
- error
1878
- );
1879
- }
1880
- }
1881
- async deleteMessages(messageIds) {
1882
- if (messageIds.length === 0) return;
1883
- const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
1884
- const threadsTableName = this.#db.getTableName(TABLE_THREADS);
1885
- try {
1886
- const placeholders = messageIds.map(() => "?").join(",");
1887
- const selectQuery = `SELECT DISTINCT thread_id FROM ${fullTableName} WHERE id IN (${placeholders})`;
1888
- const threadResults = await this.#db.executeQuery({ sql: selectQuery, params: messageIds });
1889
- const threadIds = threadResults.map((r) => r.thread_id).filter(Boolean);
1890
- const deleteQuery = createSqlBuilder().delete(fullTableName).where(`id IN (${placeholders})`, ...messageIds);
1891
- const { sql, params } = deleteQuery.build();
1892
- await this.#db.executeQuery({ sql, params });
1893
- if (threadIds.length > 0) {
1894
- const threadPlaceholders = threadIds.map(() => "?").join(",");
1895
- const threadUpdateQuery = `UPDATE ${threadsTableName} SET updatedAt = ? WHERE id IN (${threadPlaceholders})`;
1896
- const threadUpdateParams = [(/* @__PURE__ */ new Date()).toISOString(), ...threadIds];
1897
- await this.#db.executeQuery({ sql: threadUpdateQuery, params: threadUpdateParams });
1898
- }
1899
- } catch (error) {
1900
- throw new MastraError(
1901
- {
1902
- id: createStorageErrorId("CLOUDFLARE_D1", "DELETE_MESSAGES", "FAILED"),
1903
- domain: ErrorDomain.STORAGE,
1904
- category: ErrorCategory.THIRD_PARTY,
1905
- details: { messageIds: JSON.stringify(messageIds) }
1906
- },
1907
- error
1908
- );
1909
- }
1910
- }
1518
+ const queryParams = [...threadIds];
1519
+ if (resourceId) {
1520
+ query += ` AND resourceId = ?`;
1521
+ queryParams.push(resourceId);
1522
+ }
1523
+ const dateRange = filter?.dateRange;
1524
+ if (dateRange?.start) {
1525
+ const startDate = dateRange.start instanceof Date ? serializeDate(dateRange.start) : serializeDate(new Date(dateRange.start));
1526
+ const startOp = dateRange.startExclusive ? ">" : ">=";
1527
+ query += ` AND createdAt ${startOp} ?`;
1528
+ queryParams.push(startDate);
1529
+ }
1530
+ if (dateRange?.end) {
1531
+ const endDate = dateRange.end instanceof Date ? serializeDate(dateRange.end) : serializeDate(new Date(dateRange.end));
1532
+ const endOp = dateRange.endExclusive ? "<" : "<=";
1533
+ query += ` AND createdAt ${endOp} ?`;
1534
+ queryParams.push(endDate);
1535
+ }
1536
+ const metadataConditions = [];
1537
+ addSqliteMetadataFilter(metadataConditions, queryParams, metadataFilter);
1538
+ if (metadataConditions.length > 0) query += ` AND ${metadataConditions.join(" AND ")}`;
1539
+ const { field, direction } = this.parseOrderBy(orderBy, "ASC");
1540
+ if (perPage === 0 && (!include || include.length === 0)) return {
1541
+ messages: [],
1542
+ total: 0,
1543
+ page,
1544
+ perPage: perPageForResponse,
1545
+ hasMore: false
1546
+ };
1547
+ if (perPage === 0 && include && include.length > 0) {
1548
+ const includeResult = await this._getIncludedMessages(include);
1549
+ if (!Array.isArray(includeResult) || includeResult.length === 0) return {
1550
+ messages: [],
1551
+ total: 0,
1552
+ page,
1553
+ perPage: perPageForResponse,
1554
+ hasMore: false
1555
+ };
1556
+ const list = new MessageList().add(includeResult, "memory");
1557
+ return {
1558
+ messages: this._sortMessages(list.get.all.db(), field, direction),
1559
+ total: 0,
1560
+ page,
1561
+ perPage: perPageForResponse,
1562
+ hasMore: false
1563
+ };
1564
+ }
1565
+ query += ` ORDER BY "${field}" ${direction}`;
1566
+ if (perPage !== Number.MAX_SAFE_INTEGER) {
1567
+ query += ` LIMIT ? OFFSET ?`;
1568
+ queryParams.push(perPage, offset);
1569
+ }
1570
+ const results = await this.#db.executeQuery({
1571
+ sql: query,
1572
+ params: queryParams
1573
+ });
1574
+ const paginatedMessages = (isArrayOfRecords(results) ? results : []).map((message) => {
1575
+ const processedMsg = {};
1576
+ for (const [key, value] of Object.entries(message)) {
1577
+ if (key === `type` && value === `v2`) continue;
1578
+ processedMsg[key] = deserializeValue(value);
1579
+ }
1580
+ return processedMsg;
1581
+ });
1582
+ const paginatedCount = paginatedMessages.length;
1583
+ let countQuery = `SELECT count() as count FROM ${fullTableName} WHERE thread_id IN (${threadPlaceholders})`;
1584
+ const countParams = [...threadIds];
1585
+ if (resourceId) {
1586
+ countQuery += ` AND resourceId = ?`;
1587
+ countParams.push(resourceId);
1588
+ }
1589
+ if (dateRange?.start) {
1590
+ const startDate = dateRange.start instanceof Date ? serializeDate(dateRange.start) : serializeDate(new Date(dateRange.start));
1591
+ const startOp = dateRange.startExclusive ? ">" : ">=";
1592
+ countQuery += ` AND createdAt ${startOp} ?`;
1593
+ countParams.push(startDate);
1594
+ }
1595
+ if (dateRange?.end) {
1596
+ const endDate = dateRange.end instanceof Date ? serializeDate(dateRange.end) : serializeDate(new Date(dateRange.end));
1597
+ const endOp = dateRange.endExclusive ? "<" : "<=";
1598
+ countQuery += ` AND createdAt ${endOp} ?`;
1599
+ countParams.push(endDate);
1600
+ }
1601
+ const countMetadataConditions = [];
1602
+ addSqliteMetadataFilter(countMetadataConditions, countParams, metadataFilter);
1603
+ if (countMetadataConditions.length > 0) countQuery += ` AND ${countMetadataConditions.join(" AND ")}`;
1604
+ const countResult = await this.#db.executeQuery({
1605
+ sql: countQuery,
1606
+ params: countParams
1607
+ });
1608
+ const total = Number(countResult[0]?.count ?? 0);
1609
+ if (total === 0 && paginatedCount === 0 && (!include || include.length === 0)) return {
1610
+ messages: [],
1611
+ total: 0,
1612
+ page,
1613
+ perPage: perPageForResponse,
1614
+ hasMore: false
1615
+ };
1616
+ const messageIds = new Set(paginatedMessages.map((m) => m.id));
1617
+ let includeMessages = [];
1618
+ if (include && include.length > 0) {
1619
+ const includeResult = await this._getIncludedMessages(include);
1620
+ if (Array.isArray(includeResult)) {
1621
+ includeMessages = includeResult;
1622
+ for (const includeMsg of includeMessages) if (!messageIds.has(includeMsg.id)) {
1623
+ paginatedMessages.push(includeMsg);
1624
+ messageIds.add(includeMsg.id);
1625
+ }
1626
+ }
1627
+ }
1628
+ const list = new MessageList().add(paginatedMessages, "memory");
1629
+ const finalMessages = this._sortMessages(list.get.all.db(), field, direction);
1630
+ const threadIdSet = new Set(threadIds);
1631
+ const allThreadMessagesReturned = new Set(finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).map((m) => m.id)).size >= total;
1632
+ return {
1633
+ messages: finalMessages,
1634
+ total,
1635
+ page,
1636
+ perPage: perPageForResponse,
1637
+ hasMore: metadataFilter ? perPageInput !== false && offset + paginatedCount < total : perPageInput !== false && !allThreadMessagesReturned && offset + perPage < total
1638
+ };
1639
+ } catch (error) {
1640
+ const mastraError = new MastraError({
1641
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "FAILED"),
1642
+ domain: ErrorDomain.STORAGE,
1643
+ category: ErrorCategory.THIRD_PARTY,
1644
+ text: `Failed to list messages for thread ${Array.isArray(threadId) ? threadId.join(",") : threadId}: ${error instanceof Error ? error.message : String(error)}`,
1645
+ details: {
1646
+ threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
1647
+ resourceId: resourceId ?? ""
1648
+ }
1649
+ }, error);
1650
+ this.logger?.error?.(mastraError.toString());
1651
+ this.logger?.trackException?.(mastraError);
1652
+ return {
1653
+ messages: [],
1654
+ total: 0,
1655
+ page,
1656
+ perPage: perPageForResponse,
1657
+ hasMore: false
1658
+ };
1659
+ }
1660
+ }
1661
+ async updateMessages(args) {
1662
+ const { messages } = args;
1663
+ this.logger.debug("Updating messages", { count: messages.length });
1664
+ if (!messages.length) return [];
1665
+ const messageIds = messages.map((m) => m.id);
1666
+ const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
1667
+ const threadsTableName = this.#db.getTableName(TABLE_THREADS);
1668
+ try {
1669
+ const selectQuery = `SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId FROM ${fullTableName} WHERE id IN (${messageIds.map(() => "?").join(",")})`;
1670
+ const existingMessages = await this.#db.executeQuery({
1671
+ sql: selectQuery,
1672
+ params: messageIds
1673
+ });
1674
+ if (existingMessages.length === 0) return [];
1675
+ const parsedExistingMessages = existingMessages.map((msg) => {
1676
+ if (typeof msg.content === "string") try {
1677
+ msg.content = JSON.parse(msg.content);
1678
+ } catch {}
1679
+ return msg;
1680
+ });
1681
+ const threadIdsToUpdate = /* @__PURE__ */ new Set();
1682
+ const updateQueries = [];
1683
+ for (const existingMessage of parsedExistingMessages) {
1684
+ const updatePayload = messages.find((m) => m.id === existingMessage.id);
1685
+ if (!updatePayload) continue;
1686
+ const { id, ...fieldsToUpdate } = updatePayload;
1687
+ if (Object.keys(fieldsToUpdate).length === 0) continue;
1688
+ threadIdsToUpdate.add(existingMessage.threadId);
1689
+ if ("threadId" in updatePayload && updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) threadIdsToUpdate.add(updatePayload.threadId);
1690
+ const setClauses = [];
1691
+ const values = [];
1692
+ const updatableFields = { ...fieldsToUpdate };
1693
+ if (updatableFields.content) {
1694
+ const existingContent = existingMessage.content || {};
1695
+ const newContent = {
1696
+ ...existingContent,
1697
+ ...updatableFields.content,
1698
+ ...existingContent?.metadata && updatableFields.content.metadata ? { metadata: {
1699
+ ...existingContent.metadata,
1700
+ ...updatableFields.content.metadata
1701
+ } } : {}
1702
+ };
1703
+ setClauses.push(`content = ?`);
1704
+ values.push(JSON.stringify(newContent));
1705
+ delete updatableFields.content;
1706
+ }
1707
+ for (const key in updatableFields) if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
1708
+ const dbColumn = key === "threadId" ? "thread_id" : key;
1709
+ setClauses.push(`${dbColumn} = ?`);
1710
+ values.push(updatableFields[key]);
1711
+ }
1712
+ if (setClauses.length > 0) {
1713
+ values.push(id);
1714
+ const updateQuery = `UPDATE ${fullTableName} SET ${setClauses.join(", ")} WHERE id = ?`;
1715
+ updateQueries.push({
1716
+ sql: updateQuery,
1717
+ params: values
1718
+ });
1719
+ }
1720
+ }
1721
+ for (const query of updateQueries) await this.#db.executeQuery(query);
1722
+ if (threadIdsToUpdate.size > 0) {
1723
+ const threadUpdateQuery = `UPDATE ${threadsTableName} SET updatedAt = ? WHERE id IN (${Array.from(threadIdsToUpdate).map(() => "?").join(",")})`;
1724
+ const threadUpdateParams = [(/* @__PURE__ */ new Date()).toISOString(), ...Array.from(threadIdsToUpdate)];
1725
+ await this.#db.executeQuery({
1726
+ sql: threadUpdateQuery,
1727
+ params: threadUpdateParams
1728
+ });
1729
+ }
1730
+ return (await this.#db.executeQuery({
1731
+ sql: selectQuery,
1732
+ params: messageIds
1733
+ })).map((message) => {
1734
+ if (typeof message.content === "string") try {
1735
+ message.content = JSON.parse(message.content);
1736
+ } catch {}
1737
+ return message;
1738
+ });
1739
+ } catch (error) {
1740
+ throw new MastraError({
1741
+ id: createStorageErrorId("CLOUDFLARE_D1", "UPDATE_MESSAGES", "FAILED"),
1742
+ domain: ErrorDomain.STORAGE,
1743
+ category: ErrorCategory.THIRD_PARTY,
1744
+ details: { count: messages.length }
1745
+ }, error);
1746
+ }
1747
+ }
1748
+ async deleteMessages(messageIds) {
1749
+ if (messageIds.length === 0) return;
1750
+ const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
1751
+ const threadsTableName = this.#db.getTableName(TABLE_THREADS);
1752
+ try {
1753
+ const placeholders = messageIds.map(() => "?").join(",");
1754
+ const selectQuery = `SELECT DISTINCT thread_id FROM ${fullTableName} WHERE id IN (${placeholders})`;
1755
+ const threadIds = (await this.#db.executeQuery({
1756
+ sql: selectQuery,
1757
+ params: messageIds
1758
+ })).map((r) => r.thread_id).filter(Boolean);
1759
+ const { sql, params } = createSqlBuilder().delete(fullTableName).where(`id IN (${placeholders})`, ...messageIds).build();
1760
+ await this.#db.executeQuery({
1761
+ sql,
1762
+ params
1763
+ });
1764
+ if (threadIds.length > 0) {
1765
+ const threadUpdateQuery = `UPDATE ${threadsTableName} SET updatedAt = ? WHERE id IN (${threadIds.map(() => "?").join(",")})`;
1766
+ const threadUpdateParams = [(/* @__PURE__ */ new Date()).toISOString(), ...threadIds];
1767
+ await this.#db.executeQuery({
1768
+ sql: threadUpdateQuery,
1769
+ params: threadUpdateParams
1770
+ });
1771
+ }
1772
+ } catch (error) {
1773
+ throw new MastraError({
1774
+ id: createStorageErrorId("CLOUDFLARE_D1", "DELETE_MESSAGES", "FAILED"),
1775
+ domain: ErrorDomain.STORAGE,
1776
+ category: ErrorCategory.THIRD_PARTY,
1777
+ details: { messageIds: JSON.stringify(messageIds) }
1778
+ }, error);
1779
+ }
1780
+ }
1911
1781
  };
1782
+ //#endregion
1783
+ //#region src/storage/domains/scores/index.ts
1784
+ /** Applies multi-tenant scope conditions to a query builder. */
1912
1785
  function applyTenancyFilters(query, filters) {
1913
- if (filters?.organizationId !== void 0) {
1914
- query.andWhere("organizationId = ?", filters.organizationId);
1915
- }
1916
- if (filters?.projectId !== void 0) {
1917
- query.andWhere("projectId = ?", filters.projectId);
1918
- }
1786
+ if (filters?.organizationId !== void 0) query.andWhere("organizationId = ?", filters.organizationId);
1787
+ if (filters?.projectId !== void 0) query.andWhere("projectId = ?", filters.projectId);
1919
1788
  }
1920
- function transformScoreRow(row) {
1921
- return transformScoreRow$1(row, {
1922
- preferredTimestampFields: {
1923
- createdAt: "createdAtZ",
1924
- updatedAt: "updatedAtZ"
1925
- }
1926
- });
1789
+ /**
1790
+ * Cloudflare D1-specific score row transformation.
1791
+ * Uses Z-suffix timestamps (createdAtZ, updatedAtZ) when available.
1792
+ */
1793
+ function transformScoreRow$1(row) {
1794
+ return transformScoreRow(row, { preferredTimestampFields: {
1795
+ createdAt: "createdAtZ",
1796
+ updatedAt: "updatedAtZ"
1797
+ } });
1927
1798
  }
1928
1799
  var ScoresStorageD1 = class extends ScoresStorage {
1929
- #db;
1930
- constructor(config) {
1931
- super();
1932
- this.#db = new D1DB(resolveD1Config(config));
1933
- }
1934
- async init() {
1935
- await this.#db.createTable({ tableName: TABLE_SCORERS, schema: TABLE_SCHEMAS[TABLE_SCORERS] });
1936
- await this.#db.alterTable({
1937
- tableName: TABLE_SCORERS,
1938
- schema: TABLE_SCHEMAS[TABLE_SCORERS],
1939
- ifNotExists: ["organizationId", "projectId", "batchId", "datasetId", "datasetItemId"]
1940
- });
1941
- }
1942
- async dangerouslyClearAll() {
1943
- await this.#db.clearTable({ tableName: TABLE_SCORERS });
1944
- }
1945
- async getScoreById({ id }) {
1946
- try {
1947
- const fullTableName = this.#db.getTableName(TABLE_SCORERS);
1948
- const query = createSqlBuilder().select("*").from(fullTableName).where("id = ?", id);
1949
- const { sql, params } = query.build();
1950
- const result = await this.#db.executeQuery({ sql, params, first: true });
1951
- if (!result) {
1952
- return null;
1953
- }
1954
- return transformScoreRow(result);
1955
- } catch (error) {
1956
- throw new MastraError(
1957
- {
1958
- id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORE_BY_ID", "FAILED"),
1959
- domain: ErrorDomain.STORAGE,
1960
- category: ErrorCategory.THIRD_PARTY
1961
- },
1962
- error
1963
- );
1964
- }
1965
- }
1966
- async saveScore(score) {
1967
- let parsedScore;
1968
- try {
1969
- parsedScore = saveScorePayloadSchema.parse(score);
1970
- } catch (error) {
1971
- throw new MastraError(
1972
- {
1973
- id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_SCORE", "VALIDATION_FAILED"),
1974
- domain: ErrorDomain.STORAGE,
1975
- category: ErrorCategory.USER,
1976
- details: {
1977
- scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
1978
- entityId: score.entityId ?? "unknown",
1979
- entityType: score.entityType ?? "unknown",
1980
- traceId: score.traceId ?? "",
1981
- spanId: score.spanId ?? ""
1982
- }
1983
- },
1984
- error
1985
- );
1986
- }
1987
- const id = crypto.randomUUID();
1988
- try {
1989
- const fullTableName = this.#db.getTableName(TABLE_SCORERS);
1990
- const serializedRecord = {};
1991
- for (const [key, value] of Object.entries(parsedScore)) {
1992
- if (value !== null && value !== void 0) {
1993
- if (typeof value === "object") {
1994
- serializedRecord[key] = JSON.stringify(value);
1995
- } else {
1996
- serializedRecord[key] = value;
1997
- }
1998
- } else {
1999
- serializedRecord[key] = null;
2000
- }
2001
- }
2002
- const now = /* @__PURE__ */ new Date();
2003
- serializedRecord.id = id;
2004
- serializedRecord.createdAt = now.toISOString();
2005
- serializedRecord.updatedAt = now.toISOString();
2006
- const columns = Object.keys(serializedRecord);
2007
- const values = Object.values(serializedRecord);
2008
- const query = createSqlBuilder().insert(fullTableName, columns, values);
2009
- const { sql, params } = query.build();
2010
- await this.#db.executeQuery({ sql, params });
2011
- return { score: { ...parsedScore, id, createdAt: now, updatedAt: now } };
2012
- } catch (error) {
2013
- throw new MastraError(
2014
- {
2015
- id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_SCORE", "FAILED"),
2016
- domain: ErrorDomain.STORAGE,
2017
- category: ErrorCategory.THIRD_PARTY,
2018
- details: { id }
2019
- },
2020
- error
2021
- );
2022
- }
2023
- }
2024
- async listScoresByScorerId({
2025
- scorerId,
2026
- entityId,
2027
- entityType,
2028
- source,
2029
- pagination,
2030
- filters
2031
- }) {
2032
- try {
2033
- const { page, perPage: perPageInput } = pagination;
2034
- const perPage = normalizePerPage(perPageInput, 100);
2035
- const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2036
- const fullTableName = this.#db.getTableName(TABLE_SCORERS);
2037
- const countQuery = createSqlBuilder().count().from(fullTableName).where("scorerId = ?", scorerId);
2038
- if (entityId) {
2039
- countQuery.andWhere("entityId = ?", entityId);
2040
- }
2041
- if (entityType) {
2042
- countQuery.andWhere("entityType = ?", entityType);
2043
- }
2044
- if (source) {
2045
- countQuery.andWhere("source = ?", source);
2046
- }
2047
- applyTenancyFilters(countQuery, filters);
2048
- const countResult = await this.#db.executeQuery(countQuery.build());
2049
- const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
2050
- if (total === 0) {
2051
- return {
2052
- pagination: {
2053
- total: 0,
2054
- page,
2055
- perPage: perPageForResponse,
2056
- hasMore: false
2057
- },
2058
- scores: []
2059
- };
2060
- }
2061
- const end = perPageInput === false ? total : start + perPage;
2062
- const limitValue = perPageInput === false ? total : perPage;
2063
- const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("scorerId = ?", scorerId);
2064
- if (entityId) {
2065
- selectQuery.andWhere("entityId = ?", entityId);
2066
- }
2067
- if (entityType) {
2068
- selectQuery.andWhere("entityType = ?", entityType);
2069
- }
2070
- if (source) {
2071
- selectQuery.andWhere("source = ?", source);
2072
- }
2073
- applyTenancyFilters(selectQuery, filters);
2074
- selectQuery.orderBy("createdAt", "DESC").limit(limitValue).offset(start);
2075
- const { sql, params } = selectQuery.build();
2076
- const results = await this.#db.executeQuery({ sql, params });
2077
- const scores = Array.isArray(results) ? results.map(transformScoreRow) : [];
2078
- return {
2079
- pagination: {
2080
- total,
2081
- page,
2082
- perPage: perPageForResponse,
2083
- hasMore: end < total
2084
- },
2085
- scores
2086
- };
2087
- } catch (error) {
2088
- throw new MastraError(
2089
- {
2090
- id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_SCORER_ID", "FAILED"),
2091
- domain: ErrorDomain.STORAGE,
2092
- category: ErrorCategory.THIRD_PARTY
2093
- },
2094
- error
2095
- );
2096
- }
2097
- }
2098
- async listScoresByRunId({
2099
- runId,
2100
- pagination,
2101
- filters
2102
- }) {
2103
- try {
2104
- const { page, perPage: perPageInput } = pagination;
2105
- const perPage = normalizePerPage(perPageInput, 100);
2106
- const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2107
- const fullTableName = this.#db.getTableName(TABLE_SCORERS);
2108
- const countQuery = createSqlBuilder().count().from(fullTableName).where("runId = ?", runId);
2109
- applyTenancyFilters(countQuery, filters);
2110
- const countResult = await this.#db.executeQuery(countQuery.build());
2111
- const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
2112
- if (total === 0) {
2113
- return {
2114
- pagination: {
2115
- total: 0,
2116
- page,
2117
- perPage: perPageForResponse,
2118
- hasMore: false
2119
- },
2120
- scores: []
2121
- };
2122
- }
2123
- const end = perPageInput === false ? total : start + perPage;
2124
- const limitValue = perPageInput === false ? total : perPage;
2125
- const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("runId = ?", runId);
2126
- applyTenancyFilters(selectQuery, filters);
2127
- selectQuery.orderBy("createdAt", "DESC").limit(limitValue).offset(start);
2128
- const { sql, params } = selectQuery.build();
2129
- const results = await this.#db.executeQuery({ sql, params });
2130
- const scores = Array.isArray(results) ? results.map(transformScoreRow) : [];
2131
- return {
2132
- pagination: {
2133
- total,
2134
- page,
2135
- perPage: perPageForResponse,
2136
- hasMore: end < total
2137
- },
2138
- scores
2139
- };
2140
- } catch (error) {
2141
- throw new MastraError(
2142
- {
2143
- id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_RUN_ID", "FAILED"),
2144
- domain: ErrorDomain.STORAGE,
2145
- category: ErrorCategory.THIRD_PARTY
2146
- },
2147
- error
2148
- );
2149
- }
2150
- }
2151
- async listScoresByEntityId({
2152
- entityId,
2153
- entityType,
2154
- pagination,
2155
- filters
2156
- }) {
2157
- try {
2158
- const { page, perPage: perPageInput } = pagination;
2159
- const perPage = normalizePerPage(perPageInput, 100);
2160
- const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2161
- const fullTableName = this.#db.getTableName(TABLE_SCORERS);
2162
- const countQuery = createSqlBuilder().count().from(fullTableName).where("entityId = ?", entityId).andWhere("entityType = ?", entityType);
2163
- applyTenancyFilters(countQuery, filters);
2164
- const countResult = await this.#db.executeQuery(countQuery.build());
2165
- const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
2166
- if (total === 0) {
2167
- return {
2168
- pagination: {
2169
- total: 0,
2170
- page,
2171
- perPage: perPageForResponse,
2172
- hasMore: false
2173
- },
2174
- scores: []
2175
- };
2176
- }
2177
- const end = perPageInput === false ? total : start + perPage;
2178
- const limitValue = perPageInput === false ? total : perPage;
2179
- const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("entityId = ?", entityId).andWhere("entityType = ?", entityType);
2180
- applyTenancyFilters(selectQuery, filters);
2181
- selectQuery.orderBy("createdAt", "DESC").limit(limitValue).offset(start);
2182
- const { sql, params } = selectQuery.build();
2183
- const results = await this.#db.executeQuery({ sql, params });
2184
- const scores = Array.isArray(results) ? results.map(transformScoreRow) : [];
2185
- return {
2186
- pagination: {
2187
- total,
2188
- page,
2189
- perPage: perPageForResponse,
2190
- hasMore: end < total
2191
- },
2192
- scores
2193
- };
2194
- } catch (error) {
2195
- throw new MastraError(
2196
- {
2197
- id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_ENTITY_ID", "FAILED"),
2198
- domain: ErrorDomain.STORAGE,
2199
- category: ErrorCategory.THIRD_PARTY
2200
- },
2201
- error
2202
- );
2203
- }
2204
- }
2205
- async listScoresBySpan({
2206
- traceId,
2207
- spanId,
2208
- pagination,
2209
- filters
2210
- }) {
2211
- try {
2212
- const { page, perPage: perPageInput } = pagination;
2213
- const perPage = normalizePerPage(perPageInput, 100);
2214
- const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2215
- const fullTableName = this.#db.getTableName(TABLE_SCORERS);
2216
- const countQuery = createSqlBuilder().count().from(fullTableName).where("traceId = ?", traceId).andWhere("spanId = ?", spanId);
2217
- applyTenancyFilters(countQuery, filters);
2218
- const countResult = await this.#db.executeQuery(countQuery.build());
2219
- const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
2220
- if (total === 0) {
2221
- return {
2222
- pagination: {
2223
- total: 0,
2224
- page,
2225
- perPage: perPageForResponse,
2226
- hasMore: false
2227
- },
2228
- scores: []
2229
- };
2230
- }
2231
- const end = perPageInput === false ? total : start + perPage;
2232
- const limitValue = perPageInput === false ? total : perPage;
2233
- const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("traceId = ?", traceId).andWhere("spanId = ?", spanId);
2234
- applyTenancyFilters(selectQuery, filters);
2235
- selectQuery.orderBy("createdAt", "DESC").limit(limitValue).offset(start);
2236
- const { sql, params } = selectQuery.build();
2237
- const results = await this.#db.executeQuery({ sql, params });
2238
- const scores = Array.isArray(results) ? results.map(transformScoreRow) : [];
2239
- return {
2240
- pagination: {
2241
- total,
2242
- page,
2243
- perPage: perPageForResponse,
2244
- hasMore: end < total
2245
- },
2246
- scores
2247
- };
2248
- } catch (error) {
2249
- throw new MastraError(
2250
- {
2251
- id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_SPAN", "FAILED"),
2252
- domain: ErrorDomain.STORAGE,
2253
- category: ErrorCategory.THIRD_PARTY
2254
- },
2255
- error
2256
- );
2257
- }
2258
- }
1800
+ #db;
1801
+ constructor(config) {
1802
+ super();
1803
+ this.#db = new D1DB(resolveD1Config(config));
1804
+ }
1805
+ async init() {
1806
+ await this.#db.createTable({
1807
+ tableName: TABLE_SCORERS,
1808
+ schema: TABLE_SCHEMAS[TABLE_SCORERS]
1809
+ });
1810
+ await this.#db.alterTable({
1811
+ tableName: TABLE_SCORERS,
1812
+ schema: TABLE_SCHEMAS[TABLE_SCORERS],
1813
+ ifNotExists: [
1814
+ "organizationId",
1815
+ "projectId",
1816
+ "batchId",
1817
+ "datasetId",
1818
+ "datasetItemId"
1819
+ ]
1820
+ });
1821
+ }
1822
+ async dangerouslyClearAll() {
1823
+ await this.#db.clearTable({ tableName: TABLE_SCORERS });
1824
+ }
1825
+ async getScoreById({ id }) {
1826
+ try {
1827
+ const fullTableName = this.#db.getTableName(TABLE_SCORERS);
1828
+ const { sql, params } = createSqlBuilder().select("*").from(fullTableName).where("id = ?", id).build();
1829
+ const result = await this.#db.executeQuery({
1830
+ sql,
1831
+ params,
1832
+ first: true
1833
+ });
1834
+ if (!result) return null;
1835
+ return transformScoreRow$1(result);
1836
+ } catch (error) {
1837
+ throw new MastraError({
1838
+ id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORE_BY_ID", "FAILED"),
1839
+ domain: ErrorDomain.STORAGE,
1840
+ category: ErrorCategory.THIRD_PARTY
1841
+ }, error);
1842
+ }
1843
+ }
1844
+ async saveScore(score) {
1845
+ let parsedScore;
1846
+ try {
1847
+ parsedScore = saveScorePayloadSchema.parse(score);
1848
+ } catch (error) {
1849
+ throw new MastraError({
1850
+ id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_SCORE", "VALIDATION_FAILED"),
1851
+ domain: ErrorDomain.STORAGE,
1852
+ category: ErrorCategory.USER,
1853
+ details: {
1854
+ scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
1855
+ entityId: score.entityId ?? "unknown",
1856
+ entityType: score.entityType ?? "unknown",
1857
+ traceId: score.traceId ?? "",
1858
+ spanId: score.spanId ?? ""
1859
+ }
1860
+ }, error);
1861
+ }
1862
+ const id = crypto.randomUUID();
1863
+ try {
1864
+ const fullTableName = this.#db.getTableName(TABLE_SCORERS);
1865
+ const serializedRecord = {};
1866
+ for (const [key, value] of Object.entries(parsedScore)) if (value !== null && value !== void 0) if (typeof value === "object") serializedRecord[key] = JSON.stringify(value);
1867
+ else serializedRecord[key] = value;
1868
+ else serializedRecord[key] = null;
1869
+ const now = /* @__PURE__ */ new Date();
1870
+ serializedRecord.id = id;
1871
+ serializedRecord.createdAt = now.toISOString();
1872
+ serializedRecord.updatedAt = now.toISOString();
1873
+ const columns = Object.keys(serializedRecord);
1874
+ const values = Object.values(serializedRecord);
1875
+ const { sql, params } = createSqlBuilder().insert(fullTableName, columns, values).build();
1876
+ await this.#db.executeQuery({
1877
+ sql,
1878
+ params
1879
+ });
1880
+ return { score: {
1881
+ ...parsedScore,
1882
+ id,
1883
+ createdAt: now,
1884
+ updatedAt: now
1885
+ } };
1886
+ } catch (error) {
1887
+ throw new MastraError({
1888
+ id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_SCORE", "FAILED"),
1889
+ domain: ErrorDomain.STORAGE,
1890
+ category: ErrorCategory.THIRD_PARTY,
1891
+ details: { id }
1892
+ }, error);
1893
+ }
1894
+ }
1895
+ async listScoresByScorerId({ scorerId, entityId, entityType, source, pagination, filters }) {
1896
+ try {
1897
+ const { page, perPage: perPageInput } = pagination;
1898
+ const perPage = normalizePerPage(perPageInput, 100);
1899
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1900
+ const fullTableName = this.#db.getTableName(TABLE_SCORERS);
1901
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("scorerId = ?", scorerId);
1902
+ if (entityId) countQuery.andWhere("entityId = ?", entityId);
1903
+ if (entityType) countQuery.andWhere("entityType = ?", entityType);
1904
+ if (source) countQuery.andWhere("source = ?", source);
1905
+ applyTenancyFilters(countQuery, filters);
1906
+ const countResult = await this.#db.executeQuery(countQuery.build());
1907
+ const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
1908
+ if (total === 0) return {
1909
+ pagination: {
1910
+ total: 0,
1911
+ page,
1912
+ perPage: perPageForResponse,
1913
+ hasMore: false
1914
+ },
1915
+ scores: []
1916
+ };
1917
+ const end = perPageInput === false ? total : start + perPage;
1918
+ const limitValue = perPageInput === false ? total : perPage;
1919
+ const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("scorerId = ?", scorerId);
1920
+ if (entityId) selectQuery.andWhere("entityId = ?", entityId);
1921
+ if (entityType) selectQuery.andWhere("entityType = ?", entityType);
1922
+ if (source) selectQuery.andWhere("source = ?", source);
1923
+ applyTenancyFilters(selectQuery, filters);
1924
+ selectQuery.orderBy("createdAt", "DESC").limit(limitValue).offset(start);
1925
+ const { sql, params } = selectQuery.build();
1926
+ const results = await this.#db.executeQuery({
1927
+ sql,
1928
+ params
1929
+ });
1930
+ const scores = Array.isArray(results) ? results.map(transformScoreRow$1) : [];
1931
+ return {
1932
+ pagination: {
1933
+ total,
1934
+ page,
1935
+ perPage: perPageForResponse,
1936
+ hasMore: end < total
1937
+ },
1938
+ scores
1939
+ };
1940
+ } catch (error) {
1941
+ throw new MastraError({
1942
+ id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_SCORER_ID", "FAILED"),
1943
+ domain: ErrorDomain.STORAGE,
1944
+ category: ErrorCategory.THIRD_PARTY
1945
+ }, error);
1946
+ }
1947
+ }
1948
+ async listScoresByRunId({ runId, pagination, filters }) {
1949
+ try {
1950
+ const { page, perPage: perPageInput } = pagination;
1951
+ const perPage = normalizePerPage(perPageInput, 100);
1952
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1953
+ const fullTableName = this.#db.getTableName(TABLE_SCORERS);
1954
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("runId = ?", runId);
1955
+ applyTenancyFilters(countQuery, filters);
1956
+ const countResult = await this.#db.executeQuery(countQuery.build());
1957
+ const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
1958
+ if (total === 0) return {
1959
+ pagination: {
1960
+ total: 0,
1961
+ page,
1962
+ perPage: perPageForResponse,
1963
+ hasMore: false
1964
+ },
1965
+ scores: []
1966
+ };
1967
+ const end = perPageInput === false ? total : start + perPage;
1968
+ const limitValue = perPageInput === false ? total : perPage;
1969
+ const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("runId = ?", runId);
1970
+ applyTenancyFilters(selectQuery, filters);
1971
+ selectQuery.orderBy("createdAt", "DESC").limit(limitValue).offset(start);
1972
+ const { sql, params } = selectQuery.build();
1973
+ const results = await this.#db.executeQuery({
1974
+ sql,
1975
+ params
1976
+ });
1977
+ const scores = Array.isArray(results) ? results.map(transformScoreRow$1) : [];
1978
+ return {
1979
+ pagination: {
1980
+ total,
1981
+ page,
1982
+ perPage: perPageForResponse,
1983
+ hasMore: end < total
1984
+ },
1985
+ scores
1986
+ };
1987
+ } catch (error) {
1988
+ throw new MastraError({
1989
+ id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_RUN_ID", "FAILED"),
1990
+ domain: ErrorDomain.STORAGE,
1991
+ category: ErrorCategory.THIRD_PARTY
1992
+ }, error);
1993
+ }
1994
+ }
1995
+ async listScoresByEntityId({ entityId, entityType, pagination, filters }) {
1996
+ try {
1997
+ const { page, perPage: perPageInput } = pagination;
1998
+ const perPage = normalizePerPage(perPageInput, 100);
1999
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2000
+ const fullTableName = this.#db.getTableName(TABLE_SCORERS);
2001
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("entityId = ?", entityId).andWhere("entityType = ?", entityType);
2002
+ applyTenancyFilters(countQuery, filters);
2003
+ const countResult = await this.#db.executeQuery(countQuery.build());
2004
+ const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
2005
+ if (total === 0) return {
2006
+ pagination: {
2007
+ total: 0,
2008
+ page,
2009
+ perPage: perPageForResponse,
2010
+ hasMore: false
2011
+ },
2012
+ scores: []
2013
+ };
2014
+ const end = perPageInput === false ? total : start + perPage;
2015
+ const limitValue = perPageInput === false ? total : perPage;
2016
+ const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("entityId = ?", entityId).andWhere("entityType = ?", entityType);
2017
+ applyTenancyFilters(selectQuery, filters);
2018
+ selectQuery.orderBy("createdAt", "DESC").limit(limitValue).offset(start);
2019
+ const { sql, params } = selectQuery.build();
2020
+ const results = await this.#db.executeQuery({
2021
+ sql,
2022
+ params
2023
+ });
2024
+ const scores = Array.isArray(results) ? results.map(transformScoreRow$1) : [];
2025
+ return {
2026
+ pagination: {
2027
+ total,
2028
+ page,
2029
+ perPage: perPageForResponse,
2030
+ hasMore: end < total
2031
+ },
2032
+ scores
2033
+ };
2034
+ } catch (error) {
2035
+ throw new MastraError({
2036
+ id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_ENTITY_ID", "FAILED"),
2037
+ domain: ErrorDomain.STORAGE,
2038
+ category: ErrorCategory.THIRD_PARTY
2039
+ }, error);
2040
+ }
2041
+ }
2042
+ async listScoresBySpan({ traceId, spanId, pagination, filters }) {
2043
+ try {
2044
+ const { page, perPage: perPageInput } = pagination;
2045
+ const perPage = normalizePerPage(perPageInput, 100);
2046
+ const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
2047
+ const fullTableName = this.#db.getTableName(TABLE_SCORERS);
2048
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("traceId = ?", traceId).andWhere("spanId = ?", spanId);
2049
+ applyTenancyFilters(countQuery, filters);
2050
+ const countResult = await this.#db.executeQuery(countQuery.build());
2051
+ const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
2052
+ if (total === 0) return {
2053
+ pagination: {
2054
+ total: 0,
2055
+ page,
2056
+ perPage: perPageForResponse,
2057
+ hasMore: false
2058
+ },
2059
+ scores: []
2060
+ };
2061
+ const end = perPageInput === false ? total : start + perPage;
2062
+ const limitValue = perPageInput === false ? total : perPage;
2063
+ const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("traceId = ?", traceId).andWhere("spanId = ?", spanId);
2064
+ applyTenancyFilters(selectQuery, filters);
2065
+ selectQuery.orderBy("createdAt", "DESC").limit(limitValue).offset(start);
2066
+ const { sql, params } = selectQuery.build();
2067
+ const results = await this.#db.executeQuery({
2068
+ sql,
2069
+ params
2070
+ });
2071
+ const scores = Array.isArray(results) ? results.map(transformScoreRow$1) : [];
2072
+ return {
2073
+ pagination: {
2074
+ total,
2075
+ page,
2076
+ perPage: perPageForResponse,
2077
+ hasMore: end < total
2078
+ },
2079
+ scores
2080
+ };
2081
+ } catch (error) {
2082
+ throw new MastraError({
2083
+ id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_SPAN", "FAILED"),
2084
+ domain: ErrorDomain.STORAGE,
2085
+ category: ErrorCategory.THIRD_PARTY
2086
+ }, error);
2087
+ }
2088
+ }
2259
2089
  };
2090
+ //#endregion
2091
+ //#region src/storage/domains/workflows/index.ts
2260
2092
  var WorkflowsStorageD1 = class extends WorkflowsStorage {
2261
- #db;
2262
- constructor(config) {
2263
- super();
2264
- this.#db = new D1DB(resolveD1Config(config));
2265
- }
2266
- supportsConcurrentUpdates() {
2267
- return false;
2268
- }
2269
- async init() {
2270
- await this.#db.createTable({ tableName: TABLE_WORKFLOW_SNAPSHOT, schema: TABLE_SCHEMAS[TABLE_WORKFLOW_SNAPSHOT] });
2271
- }
2272
- async dangerouslyClearAll() {
2273
- await this.#db.clearTable({ tableName: TABLE_WORKFLOW_SNAPSHOT });
2274
- }
2275
- async updateWorkflowResults(_args) {
2276
- throw new Error(
2277
- "updateWorkflowResults is not implemented for Cloudflare D1 storage. D1 does not support atomic read-modify-write operations needed for concurrent workflow updates."
2278
- );
2279
- }
2280
- async updateWorkflowState(_args) {
2281
- throw new Error(
2282
- "updateWorkflowState is not implemented for Cloudflare D1 storage. D1 does not support atomic read-modify-write operations needed for concurrent workflow updates."
2283
- );
2284
- }
2285
- async persistWorkflowSnapshot({
2286
- workflowName,
2287
- runId,
2288
- resourceId,
2289
- snapshot,
2290
- createdAt,
2291
- updatedAt
2292
- }) {
2293
- const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
2294
- const now = (/* @__PURE__ */ new Date()).toISOString();
2295
- const currentSnapshot = await this.#db.load({
2296
- tableName: TABLE_WORKFLOW_SNAPSHOT,
2297
- keys: { workflow_name: workflowName, run_id: runId }
2298
- });
2299
- const persisting = currentSnapshot ? {
2300
- ...currentSnapshot,
2301
- resourceId,
2302
- snapshot: JSON.stringify(snapshot),
2303
- updatedAt: updatedAt ? updatedAt.toISOString() : now
2304
- } : {
2305
- workflow_name: workflowName,
2306
- run_id: runId,
2307
- resourceId,
2308
- snapshot,
2309
- createdAt: createdAt ? createdAt.toISOString() : now,
2310
- updatedAt: updatedAt ? updatedAt.toISOString() : now
2311
- };
2312
- const processedRecord = await this.#db.processRecord(persisting);
2313
- const columns = Object.keys(processedRecord);
2314
- const values = Object.values(processedRecord);
2315
- const updateMap = {
2316
- snapshot: "excluded.snapshot",
2317
- updatedAt: "excluded.updatedAt"
2318
- };
2319
- this.logger.debug("Persisting workflow snapshot", { workflowName, runId });
2320
- const query = createSqlBuilder().insert(fullTableName, columns, values, ["workflow_name", "run_id"], updateMap);
2321
- const { sql, params } = query.build();
2322
- try {
2323
- await this.#db.executeQuery({ sql, params });
2324
- } catch (error) {
2325
- throw new MastraError(
2326
- {
2327
- id: createStorageErrorId("CLOUDFLARE_D1", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
2328
- domain: ErrorDomain.STORAGE,
2329
- category: ErrorCategory.THIRD_PARTY,
2330
- text: `Failed to persist workflow snapshot: ${error instanceof Error ? error.message : String(error)}`,
2331
- details: { workflowName, runId }
2332
- },
2333
- error
2334
- );
2335
- }
2336
- }
2337
- async loadWorkflowSnapshot(params) {
2338
- const { workflowName, runId } = params;
2339
- this.logger.debug("Loading workflow snapshot", { workflowName, runId });
2340
- try {
2341
- const d = await this.#db.load({
2342
- tableName: TABLE_WORKFLOW_SNAPSHOT,
2343
- keys: {
2344
- workflow_name: workflowName,
2345
- run_id: runId
2346
- }
2347
- });
2348
- return d ? d.snapshot : null;
2349
- } catch (error) {
2350
- throw new MastraError(
2351
- {
2352
- id: createStorageErrorId("CLOUDFLARE_D1", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
2353
- domain: ErrorDomain.STORAGE,
2354
- category: ErrorCategory.THIRD_PARTY,
2355
- text: `Failed to load workflow snapshot: ${error instanceof Error ? error.message : String(error)}`,
2356
- details: { workflowName, runId }
2357
- },
2358
- error
2359
- );
2360
- }
2361
- }
2362
- parseWorkflowRun(row) {
2363
- let parsedSnapshot = row.snapshot;
2364
- if (typeof parsedSnapshot === "string") {
2365
- try {
2366
- parsedSnapshot = JSON.parse(row.snapshot);
2367
- } catch (e) {
2368
- this.logger.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
2369
- }
2370
- }
2371
- return {
2372
- workflowName: row.workflow_name,
2373
- runId: row.run_id,
2374
- snapshot: parsedSnapshot,
2375
- createdAt: ensureDate(row.createdAt),
2376
- updatedAt: ensureDate(row.updatedAt),
2377
- resourceId: row.resourceId
2378
- };
2379
- }
2380
- async listWorkflowRuns({
2381
- workflowName,
2382
- fromDate,
2383
- toDate,
2384
- page,
2385
- perPage,
2386
- resourceId,
2387
- status
2388
- } = {}) {
2389
- const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
2390
- try {
2391
- const builder = createSqlBuilder().select().from(fullTableName);
2392
- const countBuilder = createSqlBuilder().count().from(fullTableName);
2393
- if (workflowName) builder.whereAnd("workflow_name = ?", workflowName);
2394
- if (status) {
2395
- builder.whereAnd("json_extract(snapshot, '$.status') = ?", status);
2396
- countBuilder.whereAnd("json_extract(snapshot, '$.status') = ?", status);
2397
- }
2398
- if (resourceId) {
2399
- const hasResourceId = await this.#db.hasColumn(fullTableName, "resourceId");
2400
- if (hasResourceId) {
2401
- builder.whereAnd("resourceId = ?", resourceId);
2402
- countBuilder.whereAnd("resourceId = ?", resourceId);
2403
- } else {
2404
- this.logger.warn(`[${fullTableName}] resourceId column not found. Skipping resourceId filter.`);
2405
- }
2406
- }
2407
- if (fromDate) {
2408
- builder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
2409
- countBuilder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
2410
- }
2411
- if (toDate) {
2412
- builder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
2413
- countBuilder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
2414
- }
2415
- builder.orderBy("createdAt", "DESC");
2416
- if (typeof perPage === "number" && typeof page === "number") {
2417
- const offset = page * perPage;
2418
- builder.limit(perPage);
2419
- builder.offset(offset);
2420
- }
2421
- const { sql, params } = builder.build();
2422
- let total = 0;
2423
- if (perPage !== void 0 && page !== void 0) {
2424
- const { sql: countSql, params: countParams } = countBuilder.build();
2425
- const countResult = await this.#db.executeQuery({
2426
- sql: countSql,
2427
- params: countParams,
2428
- first: true
2429
- });
2430
- total = Number(countResult?.count ?? 0);
2431
- }
2432
- const results = await this.#db.executeQuery({ sql, params });
2433
- const runs = (isArrayOfRecords(results) ? results : []).map((row) => this.parseWorkflowRun(row));
2434
- return { runs, total: total || runs.length };
2435
- } catch (error) {
2436
- throw new MastraError(
2437
- {
2438
- id: createStorageErrorId("CLOUDFLARE_D1", "LIST_WORKFLOW_RUNS", "FAILED"),
2439
- domain: ErrorDomain.STORAGE,
2440
- category: ErrorCategory.THIRD_PARTY,
2441
- text: `Failed to retrieve workflow runs: ${error instanceof Error ? error.message : String(error)}`,
2442
- details: {
2443
- workflowName: workflowName ?? "",
2444
- resourceId: resourceId ?? ""
2445
- }
2446
- },
2447
- error
2448
- );
2449
- }
2450
- }
2451
- async getWorkflowRunById({
2452
- runId,
2453
- workflowName
2454
- }) {
2455
- const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
2456
- try {
2457
- const conditions = [];
2458
- const params = [];
2459
- if (runId) {
2460
- conditions.push("run_id = ?");
2461
- params.push(runId);
2462
- }
2463
- if (workflowName) {
2464
- conditions.push("workflow_name = ?");
2465
- params.push(workflowName);
2466
- }
2467
- const whereClause = conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : "";
2468
- const sql = `SELECT * FROM ${fullTableName} ${whereClause} ORDER BY createdAt DESC LIMIT 1`;
2469
- const result = await this.#db.executeQuery({ sql, params, first: true });
2470
- if (!result) return null;
2471
- return this.parseWorkflowRun(result);
2472
- } catch (error) {
2473
- throw new MastraError(
2474
- {
2475
- id: createStorageErrorId("CLOUDFLARE_D1", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
2476
- domain: ErrorDomain.STORAGE,
2477
- category: ErrorCategory.THIRD_PARTY,
2478
- text: `Failed to retrieve workflow run by ID: ${error instanceof Error ? error.message : String(error)}`,
2479
- details: { runId, workflowName: workflowName ?? "" }
2480
- },
2481
- error
2482
- );
2483
- }
2484
- }
2485
- async deleteWorkflowRunById({ runId, workflowName }) {
2486
- const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
2487
- try {
2488
- const sql = `DELETE FROM ${fullTableName} WHERE workflow_name = ? AND run_id = ?`;
2489
- const params = [workflowName, runId];
2490
- await this.#db.executeQuery({ sql, params });
2491
- } catch (error) {
2492
- throw new MastraError(
2493
- {
2494
- id: createStorageErrorId("CLOUDFLARE_D1", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
2495
- domain: ErrorDomain.STORAGE,
2496
- category: ErrorCategory.THIRD_PARTY,
2497
- text: `Failed to delete workflow run by ID: ${error instanceof Error ? error.message : String(error)}`,
2498
- details: { runId, workflowName }
2499
- },
2500
- error
2501
- );
2502
- }
2503
- }
2093
+ #db;
2094
+ constructor(config) {
2095
+ super();
2096
+ this.#db = new D1DB(resolveD1Config(config));
2097
+ }
2098
+ supportsConcurrentUpdates() {
2099
+ return false;
2100
+ }
2101
+ async init() {
2102
+ await this.#db.createTable({
2103
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
2104
+ schema: TABLE_SCHEMAS[TABLE_WORKFLOW_SNAPSHOT]
2105
+ });
2106
+ }
2107
+ async dangerouslyClearAll() {
2108
+ await this.#db.clearTable({ tableName: TABLE_WORKFLOW_SNAPSHOT });
2109
+ }
2110
+ async updateWorkflowResults(_args) {
2111
+ throw new Error("updateWorkflowResults is not implemented for Cloudflare D1 storage. D1 does not support atomic read-modify-write operations needed for concurrent workflow updates.");
2112
+ }
2113
+ async updateWorkflowState(_args) {
2114
+ throw new Error("updateWorkflowState is not implemented for Cloudflare D1 storage. D1 does not support atomic read-modify-write operations needed for concurrent workflow updates.");
2115
+ }
2116
+ async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt }) {
2117
+ const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
2118
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2119
+ const currentSnapshot = await this.#db.load({
2120
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
2121
+ keys: {
2122
+ workflow_name: workflowName,
2123
+ run_id: runId
2124
+ }
2125
+ });
2126
+ const persisting = currentSnapshot ? {
2127
+ ...currentSnapshot,
2128
+ resourceId,
2129
+ snapshot: JSON.stringify(snapshot),
2130
+ updatedAt: updatedAt ? updatedAt.toISOString() : now
2131
+ } : {
2132
+ workflow_name: workflowName,
2133
+ run_id: runId,
2134
+ resourceId,
2135
+ snapshot,
2136
+ createdAt: createdAt ? createdAt.toISOString() : now,
2137
+ updatedAt: updatedAt ? updatedAt.toISOString() : now
2138
+ };
2139
+ const processedRecord = await this.#db.processRecord(persisting);
2140
+ const columns = Object.keys(processedRecord);
2141
+ const values = Object.values(processedRecord);
2142
+ const updateMap = {
2143
+ snapshot: "excluded.snapshot",
2144
+ updatedAt: "excluded.updatedAt"
2145
+ };
2146
+ this.logger.debug("Persisting workflow snapshot", {
2147
+ workflowName,
2148
+ runId
2149
+ });
2150
+ const { sql, params } = createSqlBuilder().insert(fullTableName, columns, values, ["workflow_name", "run_id"], updateMap).build();
2151
+ try {
2152
+ await this.#db.executeQuery({
2153
+ sql,
2154
+ params
2155
+ });
2156
+ } catch (error) {
2157
+ throw new MastraError({
2158
+ id: createStorageErrorId("CLOUDFLARE_D1", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
2159
+ domain: ErrorDomain.STORAGE,
2160
+ category: ErrorCategory.THIRD_PARTY,
2161
+ text: `Failed to persist workflow snapshot: ${error instanceof Error ? error.message : String(error)}`,
2162
+ details: {
2163
+ workflowName,
2164
+ runId
2165
+ }
2166
+ }, error);
2167
+ }
2168
+ }
2169
+ async loadWorkflowSnapshot(params) {
2170
+ const { workflowName, runId } = params;
2171
+ this.logger.debug("Loading workflow snapshot", {
2172
+ workflowName,
2173
+ runId
2174
+ });
2175
+ try {
2176
+ const d = await this.#db.load({
2177
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
2178
+ keys: {
2179
+ workflow_name: workflowName,
2180
+ run_id: runId
2181
+ }
2182
+ });
2183
+ return d ? d.snapshot : null;
2184
+ } catch (error) {
2185
+ throw new MastraError({
2186
+ id: createStorageErrorId("CLOUDFLARE_D1", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
2187
+ domain: ErrorDomain.STORAGE,
2188
+ category: ErrorCategory.THIRD_PARTY,
2189
+ text: `Failed to load workflow snapshot: ${error instanceof Error ? error.message : String(error)}`,
2190
+ details: {
2191
+ workflowName,
2192
+ runId
2193
+ }
2194
+ }, error);
2195
+ }
2196
+ }
2197
+ parseWorkflowRun(row) {
2198
+ let parsedSnapshot = row.snapshot;
2199
+ if (typeof parsedSnapshot === "string") try {
2200
+ parsedSnapshot = JSON.parse(row.snapshot);
2201
+ } catch (e) {
2202
+ this.logger.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
2203
+ }
2204
+ return {
2205
+ workflowName: row.workflow_name,
2206
+ runId: row.run_id,
2207
+ snapshot: parsedSnapshot,
2208
+ createdAt: ensureDate(row.createdAt),
2209
+ updatedAt: ensureDate(row.updatedAt),
2210
+ resourceId: row.resourceId
2211
+ };
2212
+ }
2213
+ async listWorkflowRuns({ workflowName, fromDate, toDate, page, perPage, resourceId, status } = {}) {
2214
+ const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
2215
+ try {
2216
+ const builder = createSqlBuilder().select().from(fullTableName);
2217
+ const countBuilder = createSqlBuilder().count().from(fullTableName);
2218
+ if (workflowName) builder.whereAnd("workflow_name = ?", workflowName);
2219
+ if (status) {
2220
+ builder.whereAnd("json_extract(snapshot, '$.status') = ?", status);
2221
+ countBuilder.whereAnd("json_extract(snapshot, '$.status') = ?", status);
2222
+ }
2223
+ if (resourceId) if (await this.#db.hasColumn(fullTableName, "resourceId")) {
2224
+ builder.whereAnd("resourceId = ?", resourceId);
2225
+ countBuilder.whereAnd("resourceId = ?", resourceId);
2226
+ } else this.logger.warn(`[${fullTableName}] resourceId column not found. Skipping resourceId filter.`);
2227
+ if (fromDate) {
2228
+ builder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
2229
+ countBuilder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
2230
+ }
2231
+ if (toDate) {
2232
+ builder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
2233
+ countBuilder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
2234
+ }
2235
+ builder.orderBy("createdAt", "DESC");
2236
+ if (typeof perPage === "number" && typeof page === "number") {
2237
+ const offset = page * perPage;
2238
+ builder.limit(perPage);
2239
+ builder.offset(offset);
2240
+ }
2241
+ const { sql, params } = builder.build();
2242
+ let total = 0;
2243
+ if (perPage !== void 0 && page !== void 0) {
2244
+ const { sql: countSql, params: countParams } = countBuilder.build();
2245
+ const countResult = await this.#db.executeQuery({
2246
+ sql: countSql,
2247
+ params: countParams,
2248
+ first: true
2249
+ });
2250
+ total = Number(countResult?.count ?? 0);
2251
+ }
2252
+ const results = await this.#db.executeQuery({
2253
+ sql,
2254
+ params
2255
+ });
2256
+ const runs = (isArrayOfRecords(results) ? results : []).map((row) => this.parseWorkflowRun(row));
2257
+ return {
2258
+ runs,
2259
+ total: total || runs.length
2260
+ };
2261
+ } catch (error) {
2262
+ throw new MastraError({
2263
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_WORKFLOW_RUNS", "FAILED"),
2264
+ domain: ErrorDomain.STORAGE,
2265
+ category: ErrorCategory.THIRD_PARTY,
2266
+ text: `Failed to retrieve workflow runs: ${error instanceof Error ? error.message : String(error)}`,
2267
+ details: {
2268
+ workflowName: workflowName ?? "",
2269
+ resourceId: resourceId ?? ""
2270
+ }
2271
+ }, error);
2272
+ }
2273
+ }
2274
+ async getWorkflowRunById({ runId, workflowName }) {
2275
+ const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
2276
+ try {
2277
+ const conditions = [];
2278
+ const params = [];
2279
+ if (runId) {
2280
+ conditions.push("run_id = ?");
2281
+ params.push(runId);
2282
+ }
2283
+ if (workflowName) {
2284
+ conditions.push("workflow_name = ?");
2285
+ params.push(workflowName);
2286
+ }
2287
+ const sql = `SELECT * FROM ${fullTableName} ${conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : ""} ORDER BY createdAt DESC LIMIT 1`;
2288
+ const result = await this.#db.executeQuery({
2289
+ sql,
2290
+ params,
2291
+ first: true
2292
+ });
2293
+ if (!result) return null;
2294
+ return this.parseWorkflowRun(result);
2295
+ } catch (error) {
2296
+ throw new MastraError({
2297
+ id: createStorageErrorId("CLOUDFLARE_D1", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
2298
+ domain: ErrorDomain.STORAGE,
2299
+ category: ErrorCategory.THIRD_PARTY,
2300
+ text: `Failed to retrieve workflow run by ID: ${error instanceof Error ? error.message : String(error)}`,
2301
+ details: {
2302
+ runId,
2303
+ workflowName: workflowName ?? ""
2304
+ }
2305
+ }, error);
2306
+ }
2307
+ }
2308
+ async deleteWorkflowRunById({ runId, workflowName }) {
2309
+ const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
2310
+ try {
2311
+ const sql = `DELETE FROM ${fullTableName} WHERE workflow_name = ? AND run_id = ?`;
2312
+ const params = [workflowName, runId];
2313
+ await this.#db.executeQuery({
2314
+ sql,
2315
+ params
2316
+ });
2317
+ } catch (error) {
2318
+ throw new MastraError({
2319
+ id: createStorageErrorId("CLOUDFLARE_D1", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
2320
+ domain: ErrorDomain.STORAGE,
2321
+ category: ErrorCategory.THIRD_PARTY,
2322
+ text: `Failed to delete workflow run by ID: ${error instanceof Error ? error.message : String(error)}`,
2323
+ details: {
2324
+ runId,
2325
+ workflowName
2326
+ }
2327
+ }, error);
2328
+ }
2329
+ }
2504
2330
  };
2505
-
2506
- // src/storage/index.ts
2331
+ //#endregion
2332
+ //#region src/storage/index.ts
2333
+ /**
2334
+ * Cloudflare D1 storage adapter for Mastra.
2335
+ *
2336
+ * Access domain-specific storage via `getStore()`:
2337
+ *
2338
+ * @example
2339
+ * ```typescript
2340
+ * const storage = new D1Store({ id: 'my-store', accountId: '...', apiToken: '...', databaseId: '...' });
2341
+ *
2342
+ * // Access memory domain
2343
+ * const memory = await storage.getStore('memory');
2344
+ * await memory?.saveThread({ thread });
2345
+ *
2346
+ * // Access workflows domain
2347
+ * const workflows = await storage.getStore('workflows');
2348
+ * await workflows?.persistWorkflowSnapshot({ workflowName, runId, snapshot });
2349
+ * ```
2350
+ */
2507
2351
  var D1Store = class extends MastraCompositeStore {
2508
- client;
2509
- binding;
2510
- tablePrefix;
2511
- stores;
2512
- /**
2513
- * Creates a new D1Store instance
2514
- * @param config Configuration for D1 access (either REST API or Workers Binding API)
2515
- */
2516
- constructor(config) {
2517
- try {
2518
- super({ id: config.id, name: "D1", disableInit: config.disableInit });
2519
- if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) {
2520
- throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
2521
- }
2522
- this.tablePrefix = config.tablePrefix || "";
2523
- if ("binding" in config) {
2524
- if (!config.binding) {
2525
- throw new Error("D1 binding is required when using Workers Binding API");
2526
- }
2527
- this.binding = config.binding;
2528
- this.logger.info("Using D1 Workers Binding API");
2529
- } else if ("client" in config) {
2530
- if (!config.client) {
2531
- throw new Error("D1 client is required when using D1ClientConfig");
2532
- }
2533
- this.client = config.client;
2534
- this.logger.info("Using D1 Client");
2535
- } else {
2536
- if (!config.accountId || !config.databaseId || !config.apiToken) {
2537
- throw new Error("accountId, databaseId, and apiToken are required when using REST API");
2538
- }
2539
- const cfClient = new Cloudflare({
2540
- apiToken: config.apiToken
2541
- });
2542
- this.client = {
2543
- query: ({ sql, params }) => {
2544
- return cfClient.d1.database.query(config.databaseId, {
2545
- account_id: config.accountId,
2546
- sql,
2547
- params
2548
- });
2549
- }
2550
- };
2551
- this.logger.info("Using D1 REST API");
2552
- }
2553
- } catch (error) {
2554
- throw new MastraError(
2555
- {
2556
- id: createStorageErrorId("CLOUDFLARE_D1", "INITIALIZATION", "FAILED"),
2557
- domain: ErrorDomain.STORAGE,
2558
- category: ErrorCategory.SYSTEM,
2559
- text: "Error initializing D1Store"
2560
- },
2561
- error
2562
- );
2563
- }
2564
- let scores;
2565
- let workflows;
2566
- let memory;
2567
- let backgroundTasks;
2568
- if (this.binding) {
2569
- const domainConfig = { binding: this.binding, tablePrefix: this.tablePrefix };
2570
- scores = new ScoresStorageD1(domainConfig);
2571
- workflows = new WorkflowsStorageD1(domainConfig);
2572
- memory = new MemoryStorageD1(domainConfig);
2573
- backgroundTasks = new BackgroundTasksStorageD1(domainConfig);
2574
- } else {
2575
- const domainConfig = { client: this.client, tablePrefix: this.tablePrefix };
2576
- scores = new ScoresStorageD1(domainConfig);
2577
- workflows = new WorkflowsStorageD1(domainConfig);
2578
- memory = new MemoryStorageD1(domainConfig);
2579
- backgroundTasks = new BackgroundTasksStorageD1(domainConfig);
2580
- }
2581
- this.stores = {
2582
- scores,
2583
- workflows,
2584
- memory,
2585
- backgroundTasks
2586
- };
2587
- }
2588
- /**
2589
- * Close the database connection
2590
- * No explicit cleanup needed for D1 in either REST or Workers Binding mode
2591
- */
2592
- async close() {
2593
- this.logger.debug("Closing D1 connection");
2594
- }
2352
+ client;
2353
+ binding;
2354
+ tablePrefix;
2355
+ stores;
2356
+ /**
2357
+ * Creates a new D1Store instance
2358
+ * @param config Configuration for D1 access (either REST API or Workers Binding API)
2359
+ */
2360
+ constructor(config) {
2361
+ try {
2362
+ super({
2363
+ id: config.id,
2364
+ name: "D1",
2365
+ disableInit: config.disableInit
2366
+ });
2367
+ if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
2368
+ this.tablePrefix = config.tablePrefix || "";
2369
+ if ("binding" in config) {
2370
+ if (!config.binding) throw new Error("D1 binding is required when using Workers Binding API");
2371
+ this.binding = config.binding;
2372
+ this.logger.info("Using D1 Workers Binding API");
2373
+ } else if ("client" in config) {
2374
+ if (!config.client) throw new Error("D1 client is required when using D1ClientConfig");
2375
+ this.client = config.client;
2376
+ this.logger.info("Using D1 Client");
2377
+ } else {
2378
+ if (!config.accountId || !config.databaseId || !config.apiToken) throw new Error("accountId, databaseId, and apiToken are required when using REST API");
2379
+ const cfClient = new Cloudflare({ apiToken: config.apiToken });
2380
+ this.client = { query: ({ sql, params }) => {
2381
+ return cfClient.d1.database.query(config.databaseId, {
2382
+ account_id: config.accountId,
2383
+ sql,
2384
+ params
2385
+ });
2386
+ } };
2387
+ this.logger.info("Using D1 REST API");
2388
+ }
2389
+ } catch (error) {
2390
+ throw new MastraError({
2391
+ id: createStorageErrorId("CLOUDFLARE_D1", "INITIALIZATION", "FAILED"),
2392
+ domain: ErrorDomain.STORAGE,
2393
+ category: ErrorCategory.SYSTEM,
2394
+ text: "Error initializing D1Store"
2395
+ }, error);
2396
+ }
2397
+ let scores;
2398
+ let workflows;
2399
+ let memory;
2400
+ let backgroundTasks;
2401
+ if (this.binding) {
2402
+ const domainConfig = {
2403
+ binding: this.binding,
2404
+ tablePrefix: this.tablePrefix
2405
+ };
2406
+ scores = new ScoresStorageD1(domainConfig);
2407
+ workflows = new WorkflowsStorageD1(domainConfig);
2408
+ memory = new MemoryStorageD1(domainConfig);
2409
+ backgroundTasks = new BackgroundTasksStorageD1(domainConfig);
2410
+ } else {
2411
+ const domainConfig = {
2412
+ client: this.client,
2413
+ tablePrefix: this.tablePrefix
2414
+ };
2415
+ scores = new ScoresStorageD1(domainConfig);
2416
+ workflows = new WorkflowsStorageD1(domainConfig);
2417
+ memory = new MemoryStorageD1(domainConfig);
2418
+ backgroundTasks = new BackgroundTasksStorageD1(domainConfig);
2419
+ }
2420
+ this.stores = {
2421
+ scores,
2422
+ workflows,
2423
+ memory,
2424
+ backgroundTasks
2425
+ };
2426
+ }
2427
+ /**
2428
+ * Close the database connection
2429
+ * No explicit cleanup needed for D1 in either REST or Workers Binding mode
2430
+ */
2431
+ async close() {
2432
+ this.logger.debug("Closing D1 connection");
2433
+ }
2595
2434
  };
2596
-
2435
+ //#endregion
2597
2436
  export { BackgroundTasksStorageD1, D1Store, MemoryStorageD1, ScoresStorageD1, WorkflowsStorageD1 };
2598
- //# sourceMappingURL=index.js.map
2437
+
2599
2438
  //# sourceMappingURL=index.js.map