@mastra/cloudflare-d1 0.0.0-switch-to-core-20250424015131 → 0.0.0-taofeeqInngest-20250603090617

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,16 +1,21 @@
1
1
  import type { D1Database as D1Database_2 } from '@cloudflare/workers-types';
2
2
  import type { EvalRow } from '@mastra/core/storage';
3
+ import type { MastraMessageV1 } from '@mastra/core/memory';
4
+ import type { MastraMessageV2 } from '@mastra/core/memory';
5
+ import type { MastraMessageV2 as MastraMessageV2_2 } from '@mastra/core';
3
6
  import { MastraStorage } from '@mastra/core/storage';
4
- import type { MessageType } from '@mastra/core/memory';
5
7
  import type { StorageColumn } from '@mastra/core/storage';
6
8
  import type { StorageGetMessagesArg } from '@mastra/core/storage';
7
9
  import type { StorageThreadType } from '@mastra/core/memory';
8
10
  import type { TABLE_NAMES } from '@mastra/core/storage';
11
+ import type { WorkflowRun } from '@mastra/core/storage';
9
12
  import type { WorkflowRuns } from '@mastra/core/storage';
10
13
  import type { WorkflowRunState } from '@mastra/core/workflows';
11
14
  import type { WorkflowRunState as WorkflowRunState_2 } from '@mastra/core';
12
15
 
13
- export declare const createSampleMessage: (threadId: string) => any;
16
+ export declare const checkWorkflowSnapshot: (snapshot: WorkflowRunState_2 | string, stepId: string, status: string) => void;
17
+
18
+ export declare const createSampleMessage: (threadId: string, parts?: MastraMessageV2_2["content"]["parts"]) => MastraMessageV2_2;
14
19
 
15
20
  export declare const createSampleThread: () => {
16
21
  id: string;
@@ -51,7 +56,11 @@ export declare const createSampleTrace: (name: string, scope?: string, attribute
51
56
  createdAt: string;
52
57
  };
53
58
 
54
- export declare const createSampleWorkflowSnapshot: (threadId: string) => WorkflowRunState_2;
59
+ export declare const createSampleWorkflowSnapshot: (threadId: string, status: string, createdAt?: Date) => {
60
+ snapshot: WorkflowRunState_2;
61
+ runId: string;
62
+ stepId: string;
63
+ };
55
64
 
56
65
  export declare function createSqlBuilder(): SqlBuilder;
57
66
 
@@ -84,7 +93,6 @@ declare class D1Store extends MastraStorage {
84
93
  constructor(config: D1StoreConfig);
85
94
  private getTableName;
86
95
  private formatSqlParams;
87
- private createIndexIfNotExists;
88
96
  private executeWorkersBindingQuery;
89
97
  private executeRestQuery;
90
98
  /**
@@ -131,10 +139,20 @@ declare class D1Store extends MastraStorage {
131
139
  deleteThread({ threadId }: {
132
140
  threadId: string;
133
141
  }): Promise<void>;
134
- saveMessages({ messages }: {
135
- messages: MessageType[];
136
- }): Promise<MessageType[]>;
137
- getMessages<T = MessageType>({ threadId, selectBy }: StorageGetMessagesArg): Promise<T[]>;
142
+ saveMessages(args: {
143
+ messages: MastraMessageV1[];
144
+ format?: undefined | 'v1';
145
+ }): Promise<MastraMessageV1[]>;
146
+ saveMessages(args: {
147
+ messages: MastraMessageV2[];
148
+ format: 'v2';
149
+ }): Promise<MastraMessageV2[]>;
150
+ getMessages(args: StorageGetMessagesArg & {
151
+ format?: 'v1';
152
+ }): Promise<MastraMessageV1[]>;
153
+ getMessages(args: StorageGetMessagesArg & {
154
+ format: 'v2';
155
+ }): Promise<MastraMessageV2[]>;
138
156
  persistWorkflowSnapshot({ workflowName, runId, snapshot, }: {
139
157
  workflowName: string;
140
158
  runId: string;
@@ -153,21 +171,30 @@ declare class D1Store extends MastraStorage {
153
171
  tableName: TABLE_NAMES;
154
172
  records: Record<string, any>[];
155
173
  }): Promise<void>;
156
- getTraces({ name, scope, page, perPage, attributes, }: {
174
+ getTraces({ name, scope, page, perPage, attributes, fromDate, toDate, }: {
157
175
  name?: string;
158
176
  scope?: string;
159
177
  page: number;
160
178
  perPage: number;
161
179
  attributes?: Record<string, string>;
180
+ fromDate?: Date;
181
+ toDate?: Date;
162
182
  }): Promise<Record<string, any>[]>;
163
183
  getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]>;
164
- getWorkflowRuns(_args?: {
184
+ private parseWorkflowRun;
185
+ private hasColumn;
186
+ getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId, }?: {
165
187
  workflowName?: string;
166
188
  fromDate?: Date;
167
189
  toDate?: Date;
168
190
  limit?: number;
169
191
  offset?: number;
192
+ resourceId?: string;
170
193
  }): Promise<WorkflowRuns>;
194
+ getWorkflowRunById({ runId, workflowName, }: {
195
+ runId: string;
196
+ workflowName?: string;
197
+ }): Promise<WorkflowRun | null>;
171
198
  /**
172
199
  * Close the database connection
173
200
  * No explicit cleanup needed for D1 in either REST or Workers Binding mode
@@ -196,9 +223,30 @@ declare interface D1WorkersConfig {
196
223
  export { D1WorkersConfig }
197
224
  export { D1WorkersConfig as D1WorkersConfig_alias_1 }
198
225
 
226
+ /**
227
+ * Parses and returns a valid SQL SELECT column identifier.
228
+ * Allows a single identifier (letters, numbers, underscores), or '*', optionally with 'AS alias'.
229
+ *
230
+ * @param column - The column identifier string to parse.
231
+ * @returns The validated column identifier as a branded type.
232
+ * @throws {Error} If invalid.
233
+ *
234
+ * @example
235
+ * const col = parseSelectIdentifier('user_id'); // Ok
236
+ * parseSelectIdentifier('user_id AS uid'); // Ok
237
+ * parseSelectIdentifier('*'); // Ok
238
+ * parseSelectIdentifier('user id'); // Throws error
239
+ */
240
+ export declare function parseSelectIdentifier(column: string): SelectIdentifier;
241
+
199
242
  export declare const retryUntil: <T>(fn: () => Promise<T>, condition: (result: T) => boolean, timeout?: number, // REST API needs longer timeout due to higher latency
200
243
  interval?: number) => Promise<T>;
201
244
 
245
+ /** Represents a validated SQL SELECT column identifier (or '*', optionally with 'AS alias'). */
246
+ declare type SelectIdentifier = string & {
247
+ __brand: 'SelectIdentifier';
248
+ };
249
+
202
250
  /**
203
251
  * SQL Builder class for constructing type-safe SQL queries
204
252
  * This helps create maintainable and secure SQL queries with proper parameter handling
@@ -226,6 +274,7 @@ export declare class SqlBuilder {
226
274
  orderBy(column: string, direction?: 'ASC' | 'DESC'): SqlBuilder;
227
275
  limit(count: number): SqlBuilder;
228
276
  offset(count: number): SqlBuilder;
277
+ count(): SqlBuilder;
229
278
  /**
230
279
  * Insert a row, or update specific columns on conflict (upsert).
231
280
  * @param table Table name
@@ -261,7 +310,6 @@ export declare class SqlBuilder {
261
310
  * @returns The builder instance
262
311
  */
263
312
  createIndex(indexName: string, tableName: string, columnName: string, indexType?: string): SqlBuilder;
264
- raw(sql: string, ...params: SqlParam[]): SqlBuilder;
265
313
  /**
266
314
  * Add a LIKE condition to the query
267
315
  * @param column The column to check
@@ -299,7 +347,7 @@ export declare type SqlParam = string | number | boolean | null | undefined;
299
347
  /**
300
348
  * Interface for SQL query options with generic type support
301
349
  */
302
- declare interface SqlQueryOptions {
350
+ export declare interface SqlQueryOptions {
303
351
  /** SQL query to execute */
304
352
  sql: string;
305
353
  /** Parameters to bind to the query */
@@ -307,7 +355,5 @@ declare interface SqlQueryOptions {
307
355
  /** Whether to return only the first result */
308
356
  first?: boolean;
309
357
  }
310
- export { SqlQueryOptions }
311
- export { SqlQueryOptions as SqlQueryOptions_alias_1 }
312
358
 
313
359
  export { }
@@ -1,16 +1,21 @@
1
1
  import type { D1Database as D1Database_2 } from '@cloudflare/workers-types';
2
2
  import type { EvalRow } from '@mastra/core/storage';
3
+ import type { MastraMessageV1 } from '@mastra/core/memory';
4
+ import type { MastraMessageV2 } from '@mastra/core/memory';
5
+ import type { MastraMessageV2 as MastraMessageV2_2 } from '@mastra/core';
3
6
  import { MastraStorage } from '@mastra/core/storage';
4
- import type { MessageType } from '@mastra/core/memory';
5
7
  import type { StorageColumn } from '@mastra/core/storage';
6
8
  import type { StorageGetMessagesArg } from '@mastra/core/storage';
7
9
  import type { StorageThreadType } from '@mastra/core/memory';
8
10
  import type { TABLE_NAMES } from '@mastra/core/storage';
11
+ import type { WorkflowRun } from '@mastra/core/storage';
9
12
  import type { WorkflowRuns } from '@mastra/core/storage';
10
13
  import type { WorkflowRunState } from '@mastra/core/workflows';
11
14
  import type { WorkflowRunState as WorkflowRunState_2 } from '@mastra/core';
12
15
 
13
- export declare const createSampleMessage: (threadId: string) => any;
16
+ export declare const checkWorkflowSnapshot: (snapshot: WorkflowRunState_2 | string, stepId: string, status: string) => void;
17
+
18
+ export declare const createSampleMessage: (threadId: string, parts?: MastraMessageV2_2["content"]["parts"]) => MastraMessageV2_2;
14
19
 
15
20
  export declare const createSampleThread: () => {
16
21
  id: string;
@@ -51,7 +56,11 @@ export declare const createSampleTrace: (name: string, scope?: string, attribute
51
56
  createdAt: string;
52
57
  };
53
58
 
54
- export declare const createSampleWorkflowSnapshot: (threadId: string) => WorkflowRunState_2;
59
+ export declare const createSampleWorkflowSnapshot: (threadId: string, status: string, createdAt?: Date) => {
60
+ snapshot: WorkflowRunState_2;
61
+ runId: string;
62
+ stepId: string;
63
+ };
55
64
 
56
65
  export declare function createSqlBuilder(): SqlBuilder;
57
66
 
@@ -84,7 +93,6 @@ declare class D1Store extends MastraStorage {
84
93
  constructor(config: D1StoreConfig);
85
94
  private getTableName;
86
95
  private formatSqlParams;
87
- private createIndexIfNotExists;
88
96
  private executeWorkersBindingQuery;
89
97
  private executeRestQuery;
90
98
  /**
@@ -131,10 +139,20 @@ declare class D1Store extends MastraStorage {
131
139
  deleteThread({ threadId }: {
132
140
  threadId: string;
133
141
  }): Promise<void>;
134
- saveMessages({ messages }: {
135
- messages: MessageType[];
136
- }): Promise<MessageType[]>;
137
- getMessages<T = MessageType>({ threadId, selectBy }: StorageGetMessagesArg): Promise<T[]>;
142
+ saveMessages(args: {
143
+ messages: MastraMessageV1[];
144
+ format?: undefined | 'v1';
145
+ }): Promise<MastraMessageV1[]>;
146
+ saveMessages(args: {
147
+ messages: MastraMessageV2[];
148
+ format: 'v2';
149
+ }): Promise<MastraMessageV2[]>;
150
+ getMessages(args: StorageGetMessagesArg & {
151
+ format?: 'v1';
152
+ }): Promise<MastraMessageV1[]>;
153
+ getMessages(args: StorageGetMessagesArg & {
154
+ format: 'v2';
155
+ }): Promise<MastraMessageV2[]>;
138
156
  persistWorkflowSnapshot({ workflowName, runId, snapshot, }: {
139
157
  workflowName: string;
140
158
  runId: string;
@@ -153,21 +171,30 @@ declare class D1Store extends MastraStorage {
153
171
  tableName: TABLE_NAMES;
154
172
  records: Record<string, any>[];
155
173
  }): Promise<void>;
156
- getTraces({ name, scope, page, perPage, attributes, }: {
174
+ getTraces({ name, scope, page, perPage, attributes, fromDate, toDate, }: {
157
175
  name?: string;
158
176
  scope?: string;
159
177
  page: number;
160
178
  perPage: number;
161
179
  attributes?: Record<string, string>;
180
+ fromDate?: Date;
181
+ toDate?: Date;
162
182
  }): Promise<Record<string, any>[]>;
163
183
  getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]>;
164
- getWorkflowRuns(_args?: {
184
+ private parseWorkflowRun;
185
+ private hasColumn;
186
+ getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId, }?: {
165
187
  workflowName?: string;
166
188
  fromDate?: Date;
167
189
  toDate?: Date;
168
190
  limit?: number;
169
191
  offset?: number;
192
+ resourceId?: string;
170
193
  }): Promise<WorkflowRuns>;
194
+ getWorkflowRunById({ runId, workflowName, }: {
195
+ runId: string;
196
+ workflowName?: string;
197
+ }): Promise<WorkflowRun | null>;
171
198
  /**
172
199
  * Close the database connection
173
200
  * No explicit cleanup needed for D1 in either REST or Workers Binding mode
@@ -196,9 +223,30 @@ declare interface D1WorkersConfig {
196
223
  export { D1WorkersConfig }
197
224
  export { D1WorkersConfig as D1WorkersConfig_alias_1 }
198
225
 
226
+ /**
227
+ * Parses and returns a valid SQL SELECT column identifier.
228
+ * Allows a single identifier (letters, numbers, underscores), or '*', optionally with 'AS alias'.
229
+ *
230
+ * @param column - The column identifier string to parse.
231
+ * @returns The validated column identifier as a branded type.
232
+ * @throws {Error} If invalid.
233
+ *
234
+ * @example
235
+ * const col = parseSelectIdentifier('user_id'); // Ok
236
+ * parseSelectIdentifier('user_id AS uid'); // Ok
237
+ * parseSelectIdentifier('*'); // Ok
238
+ * parseSelectIdentifier('user id'); // Throws error
239
+ */
240
+ export declare function parseSelectIdentifier(column: string): SelectIdentifier;
241
+
199
242
  export declare const retryUntil: <T>(fn: () => Promise<T>, condition: (result: T) => boolean, timeout?: number, // REST API needs longer timeout due to higher latency
200
243
  interval?: number) => Promise<T>;
201
244
 
245
+ /** Represents a validated SQL SELECT column identifier (or '*', optionally with 'AS alias'). */
246
+ declare type SelectIdentifier = string & {
247
+ __brand: 'SelectIdentifier';
248
+ };
249
+
202
250
  /**
203
251
  * SQL Builder class for constructing type-safe SQL queries
204
252
  * This helps create maintainable and secure SQL queries with proper parameter handling
@@ -226,6 +274,7 @@ export declare class SqlBuilder {
226
274
  orderBy(column: string, direction?: 'ASC' | 'DESC'): SqlBuilder;
227
275
  limit(count: number): SqlBuilder;
228
276
  offset(count: number): SqlBuilder;
277
+ count(): SqlBuilder;
229
278
  /**
230
279
  * Insert a row, or update specific columns on conflict (upsert).
231
280
  * @param table Table name
@@ -261,7 +310,6 @@ export declare class SqlBuilder {
261
310
  * @returns The builder instance
262
311
  */
263
312
  createIndex(indexName: string, tableName: string, columnName: string, indexType?: string): SqlBuilder;
264
- raw(sql: string, ...params: SqlParam[]): SqlBuilder;
265
313
  /**
266
314
  * Add a LIKE condition to the query
267
315
  * @param column The column to check
@@ -299,7 +347,7 @@ export declare type SqlParam = string | number | boolean | null | undefined;
299
347
  /**
300
348
  * Interface for SQL query options with generic type support
301
349
  */
302
- declare interface SqlQueryOptions {
350
+ export declare interface SqlQueryOptions {
303
351
  /** SQL query to execute */
304
352
  sql: string;
305
353
  /** Parameters to bind to the query */
@@ -307,7 +355,5 @@ declare interface SqlQueryOptions {
307
355
  /** Whether to return only the first result */
308
356
  first?: boolean;
309
357
  }
310
- export { SqlQueryOptions }
311
- export { SqlQueryOptions as SqlQueryOptions_alias_1 }
312
358
 
313
359
  export { }
package/dist/index.cjs CHANGED
@@ -1,15 +1,15 @@
1
1
  'use strict';
2
2
 
3
+ var agent = require('@mastra/core/agent');
3
4
  var storage = require('@mastra/core/storage');
4
5
  var Cloudflare = require('cloudflare');
6
+ var utils = require('@mastra/core/utils');
5
7
 
6
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
9
 
8
10
  var Cloudflare__default = /*#__PURE__*/_interopDefault(Cloudflare);
9
11
 
10
12
  // src/storage/index.ts
11
-
12
- // src/storage/sql-builder.ts
13
13
  var SqlBuilder = class {
14
14
  sql = "";
15
15
  params = [];
@@ -19,12 +19,15 @@ var SqlBuilder = class {
19
19
  if (!columns || Array.isArray(columns) && columns.length === 0) {
20
20
  this.sql = "SELECT *";
21
21
  } else {
22
- this.sql = `SELECT ${Array.isArray(columns) ? columns.join(", ") : columns}`;
22
+ const cols = Array.isArray(columns) ? columns : [columns];
23
+ const parsedCols = cols.map((col) => parseSelectIdentifier(col));
24
+ this.sql = `SELECT ${parsedCols.join(", ")}`;
23
25
  }
24
26
  return this;
25
27
  }
26
28
  from(table) {
27
- this.sql += ` FROM ${table}`;
29
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
30
+ this.sql += ` FROM ${parsedTableName}`;
28
31
  return this;
29
32
  }
30
33
  /**
@@ -61,7 +64,11 @@ var SqlBuilder = class {
61
64
  return this;
62
65
  }
63
66
  orderBy(column, direction = "ASC") {
64
- this.sql += ` ORDER BY ${column} ${direction}`;
67
+ const parsedColumn = utils.parseSqlIdentifier(column, "column name");
68
+ if (!["ASC", "DESC"].includes(direction)) {
69
+ throw new Error(`Invalid sort direction: ${direction}`);
70
+ }
71
+ this.sql += ` ORDER BY ${parsedColumn} ${direction}`;
65
72
  return this;
66
73
  }
67
74
  limit(count) {
@@ -74,6 +81,10 @@ var SqlBuilder = class {
74
81
  this.params.push(count);
75
82
  return this;
76
83
  }
84
+ count() {
85
+ this.sql += "SELECT COUNT(*) AS count";
86
+ return this;
87
+ }
77
88
  /**
78
89
  * Insert a row, or update specific columns on conflict (upsert).
79
90
  * @param table Table name
@@ -83,27 +94,33 @@ var SqlBuilder = class {
83
94
  * @param updateMap Object mapping columns to update to their new value (e.g. { name: 'excluded.name' })
84
95
  */
85
96
  insert(table, columns, values, conflictColumns, updateMap) {
86
- const placeholders = columns.map(() => "?").join(", ");
97
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
98
+ const parsedColumns = columns.map((col) => utils.parseSqlIdentifier(col, "column name"));
99
+ const placeholders = parsedColumns.map(() => "?").join(", ");
87
100
  if (conflictColumns && updateMap) {
101
+ const parsedConflictColumns = conflictColumns.map((col) => utils.parseSqlIdentifier(col, "column name"));
88
102
  const updateClause = Object.entries(updateMap).map(([col, expr]) => `${col} = ${expr}`).join(", ");
89
- this.sql = `INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${conflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
103
+ this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${parsedConflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
90
104
  this.params.push(...values);
91
105
  return this;
92
106
  }
93
- this.sql = `INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders})`;
107
+ this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders})`;
94
108
  this.params.push(...values);
95
109
  return this;
96
110
  }
97
111
  // Update operations
98
112
  update(table, columns, values) {
99
- const setClause = columns.map((col) => `${col} = ?`).join(", ");
100
- this.sql = `UPDATE ${table} SET ${setClause}`;
113
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
114
+ const parsedColumns = columns.map((col) => utils.parseSqlIdentifier(col, "column name"));
115
+ const setClause = parsedColumns.map((col) => `${col} = ?`).join(", ");
116
+ this.sql = `UPDATE ${parsedTableName} SET ${setClause}`;
101
117
  this.params.push(...values);
102
118
  return this;
103
119
  }
104
120
  // Delete operations
105
121
  delete(table) {
106
- this.sql = `DELETE FROM ${table}`;
122
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
123
+ this.sql = `DELETE FROM ${parsedTableName}`;
107
124
  return this;
108
125
  }
109
126
  /**
@@ -114,9 +131,16 @@ var SqlBuilder = class {
114
131
  * @returns The builder instance
115
132
  */
116
133
  createTable(table, columnDefinitions, tableConstraints) {
117
- const columns = columnDefinitions.join(", ");
134
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
135
+ const parsedColumnDefinitions = columnDefinitions.map((def) => {
136
+ const colName = def.split(/\s+/)[0];
137
+ if (!colName) throw new Error("Empty column name in definition");
138
+ utils.parseSqlIdentifier(colName, "column name");
139
+ return def;
140
+ });
141
+ const columns = parsedColumnDefinitions.join(", ");
118
142
  const constraints = tableConstraints && tableConstraints.length > 0 ? ", " + tableConstraints.join(", ") : "";
119
- this.sql = `CREATE TABLE IF NOT EXISTS ${table} (${columns}${constraints})`;
143
+ this.sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns}${constraints})`;
120
144
  return this;
121
145
  }
122
146
  /**
@@ -139,13 +163,10 @@ var SqlBuilder = class {
139
163
  * @returns The builder instance
140
164
  */
141
165
  createIndex(indexName, tableName, columnName, indexType = "") {
142
- this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${columnName})`;
143
- return this;
144
- }
145
- // Raw SQL with params
146
- raw(sql, ...params) {
147
- this.sql = sql;
148
- this.params.push(...params);
166
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
167
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
168
+ const parsedColumnName = utils.parseSqlIdentifier(columnName, "column name");
169
+ this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${parsedIndexName} ON ${parsedTableName}(${parsedColumnName})`;
149
170
  return this;
150
171
  }
151
172
  /**
@@ -155,11 +176,12 @@ var SqlBuilder = class {
155
176
  * @param exact If true, will not add % wildcards
156
177
  */
157
178
  like(column, value, exact = false) {
179
+ const parsedColumnName = utils.parseSqlIdentifier(column, "column name");
158
180
  const likeValue = exact ? value : `%${value}%`;
159
181
  if (this.whereAdded) {
160
- this.sql += ` AND ${column} LIKE ?`;
182
+ this.sql += ` AND ${parsedColumnName} LIKE ?`;
161
183
  } else {
162
- this.sql += ` WHERE ${column} LIKE ?`;
184
+ this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
163
185
  this.whereAdded = true;
164
186
  }
165
187
  this.params.push(likeValue);
@@ -172,11 +194,13 @@ var SqlBuilder = class {
172
194
  * @param value The value to match
173
195
  */
174
196
  jsonLike(column, key, value) {
175
- const jsonPattern = `%"${key}":"${value}"%`;
197
+ const parsedColumnName = utils.parseSqlIdentifier(column, "column name");
198
+ const parsedKey = utils.parseSqlIdentifier(key, "key name");
199
+ const jsonPattern = `%"${parsedKey}":"${value}"%`;
176
200
  if (this.whereAdded) {
177
- this.sql += ` AND ${column} LIKE ?`;
201
+ this.sql += ` AND ${parsedColumnName} LIKE ?`;
178
202
  } else {
179
- this.sql += ` WHERE ${column} LIKE ?`;
203
+ this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
180
204
  this.whereAdded = true;
181
205
  }
182
206
  this.params.push(jsonPattern);
@@ -206,6 +230,15 @@ var SqlBuilder = class {
206
230
  function createSqlBuilder() {
207
231
  return new SqlBuilder();
208
232
  }
233
+ var SQL_IDENTIFIER_PATTERN = /^[a-zA-Z0-9_]+(\s+AS\s+[a-zA-Z0-9_]+)?$/;
234
+ function parseSelectIdentifier(column) {
235
+ if (column !== "*" && !SQL_IDENTIFIER_PATTERN.test(column)) {
236
+ throw new Error(
237
+ `Invalid column name: "${column}". Must be "*" or a valid identifier (letters, numbers, underscores), optionally with "AS alias".`
238
+ );
239
+ }
240
+ return column;
241
+ }
209
242
 
210
243
  // src/storage/index.ts
211
244
  function isArrayOfRecords(value) {
@@ -224,6 +257,9 @@ var D1Store = class extends storage.MastraStorage {
224
257
  */
225
258
  constructor(config) {
226
259
  super({ name: "D1" });
260
+ if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) {
261
+ throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
262
+ }
227
263
  this.tablePrefix = config.tablePrefix || "";
228
264
  if ("binding" in config) {
229
265
  if (!config.binding) {
@@ -250,30 +286,6 @@ var D1Store = class extends storage.MastraStorage {
250
286
  formatSqlParams(params) {
251
287
  return params.map((p) => p === void 0 || p === null ? null : p);
252
288
  }
253
- // Helper method to create SQL indexes for better query performance
254
- async createIndexIfNotExists(tableName, columnName, indexType = "") {
255
- const fullTableName = this.getTableName(tableName);
256
- const indexName = `idx_${tableName}_${columnName}`;
257
- try {
258
- const checkQuery = createSqlBuilder().checkIndexExists(indexName, fullTableName);
259
- const { sql: checkSql, params: checkParams } = checkQuery.build();
260
- const indexExists = await this.executeQuery({
261
- sql: checkSql,
262
- params: checkParams,
263
- first: true
264
- });
265
- if (!indexExists) {
266
- const createQuery = createSqlBuilder().createIndex(indexName, fullTableName, columnName, indexType);
267
- const { sql: createSql, params: createParams } = createQuery.build();
268
- await this.executeQuery({ sql: createSql, params: createParams });
269
- this.logger.debug(`Created index ${indexName} on ${fullTableName}(${columnName})`);
270
- }
271
- } catch (error) {
272
- this.logger.error(`Error creating index on ${fullTableName}(${columnName}):`, {
273
- message: error instanceof Error ? error.message : String(error)
274
- });
275
- }
276
- }
277
289
  async executeWorkersBindingQuery({
278
290
  sql,
279
291
  params = [],
@@ -496,7 +508,8 @@ var D1Store = class extends storage.MastraStorage {
496
508
  try {
497
509
  await this.executeQuery({ sql, params });
498
510
  } catch (error) {
499
- this.logger.error(`Error inserting into ${fullTableName}:`, { error });
511
+ const message = error instanceof Error ? error.message : String(error);
512
+ this.logger.error(`Error inserting into ${fullTableName}:`, { message });
500
513
  throw new Error(`Failed to insert into ${fullTableName}: ${error}`);
501
514
  }
502
515
  }
@@ -594,7 +607,8 @@ var D1Store = class extends storage.MastraStorage {
594
607
  await this.executeQuery({ sql, params });
595
608
  return thread;
596
609
  } catch (error) {
597
- this.logger.error(`Error saving thread to ${fullTableName}:`, { error });
610
+ const message = error instanceof Error ? error.message : String(error);
611
+ this.logger.error(`Error saving thread to ${fullTableName}:`, { message });
598
612
  throw error;
599
613
  }
600
614
  }
@@ -628,7 +642,8 @@ var D1Store = class extends storage.MastraStorage {
628
642
  updatedAt: /* @__PURE__ */ new Date()
629
643
  };
630
644
  } catch (error) {
631
- this.logger.error("Error updating thread:", { error });
645
+ const message = error instanceof Error ? error.message : String(error);
646
+ this.logger.error("Error updating thread:", { message });
632
647
  throw error;
633
648
  }
634
649
  }
@@ -649,8 +664,8 @@ var D1Store = class extends storage.MastraStorage {
649
664
  throw new Error(`Failed to delete thread ${threadId}: ${error}`);
650
665
  }
651
666
  }
652
- // Thread and message management methods
653
- async saveMessages({ messages }) {
667
+ async saveMessages(args) {
668
+ const { messages, format = "v1" } = args;
654
669
  if (messages.length === 0) return [];
655
670
  try {
656
671
  const now = /* @__PURE__ */ new Date();
@@ -672,7 +687,7 @@ var D1Store = class extends storage.MastraStorage {
672
687
  content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
673
688
  createdAt: createdAt.toISOString(),
674
689
  role: message.role,
675
- type: message.type
690
+ type: message.type || "v2"
676
691
  };
677
692
  });
678
693
  await this.batchInsert({
@@ -680,13 +695,19 @@ var D1Store = class extends storage.MastraStorage {
680
695
  records: messagesToInsert
681
696
  });
682
697
  this.logger.debug(`Saved ${messages.length} messages`);
683
- return messages;
698
+ const list = new agent.MessageList().add(messages, "memory");
699
+ if (format === `v2`) return list.get.all.v2();
700
+ return list.get.all.v1();
684
701
  } catch (error) {
685
702
  this.logger.error("Error saving messages:", { message: error instanceof Error ? error.message : String(error) });
686
703
  throw error;
687
704
  }
688
705
  }
689
- async getMessages({ threadId, selectBy }) {
706
+ async getMessages({
707
+ threadId,
708
+ selectBy,
709
+ format
710
+ }) {
690
711
  const fullTableName = this.getTableName(storage.TABLE_MESSAGES);
691
712
  const limit = typeof selectBy?.last === "number" ? selectBy.last : 40;
692
713
  const include = selectBy?.include || [];
@@ -710,7 +731,7 @@ var D1Store = class extends storage.MastraStorage {
710
731
  m.role,
711
732
  m.type,
712
733
  m.createdAt,
713
- m.thread_id AS "threadId"
734
+ m.thread_id AS threadId
714
735
  FROM ordered_messages m
715
736
  WHERE m.id IN (${includeIds.map(() => "?").join(",")})
716
737
  OR EXISTS (
@@ -737,7 +758,7 @@ var D1Store = class extends storage.MastraStorage {
737
758
  if (Array.isArray(includeResult)) messages.push(...includeResult);
738
759
  }
739
760
  const excludeIds = messages.map((m) => m.id);
740
- let query = createSqlBuilder().select(["id", "content", "role", "type", '"createdAt"', 'thread_id AS "threadId"']).from(fullTableName).where("thread_id = ?", threadId).andWhere(`id NOT IN (${excludeIds.map(() => "?").join(",")})`, ...excludeIds).orderBy("createdAt", "DESC").limit(limit);
761
+ let query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId"]).from(fullTableName).where("thread_id = ?", threadId).andWhere(`id NOT IN (${excludeIds.map(() => "?").join(",")})`, ...excludeIds).orderBy("createdAt", "DESC").limit(limit);
741
762
  const { sql, params } = query.build();
742
763
  const result = await this.executeQuery({ sql, params });
743
764
  if (Array.isArray(result)) messages.push(...result);
@@ -751,12 +772,15 @@ var D1Store = class extends storage.MastraStorage {
751
772
  const processedMessages = messages.map((message) => {
752
773
  const processedMsg = {};
753
774
  for (const [key, value] of Object.entries(message)) {
775
+ if (key === `type` && value === `v2`) continue;
754
776
  processedMsg[key] = this.deserializeValue(value);
755
777
  }
756
778
  return processedMsg;
757
779
  });
758
780
  this.logger.debug(`Retrieved ${messages.length} messages for thread ${threadId}`);
759
- return processedMessages;
781
+ const list = new agent.MessageList().add(processedMessages, "memory");
782
+ if (format === `v2`) return list.get.all.v2();
783
+ return list.get.all.v1();
760
784
  } catch (error) {
761
785
  this.logger.error("Error retrieving messages for thread", {
762
786
  threadId,
@@ -862,7 +886,9 @@ var D1Store = class extends storage.MastraStorage {
862
886
  scope,
863
887
  page,
864
888
  perPage,
865
- attributes
889
+ attributes,
890
+ fromDate,
891
+ toDate
866
892
  }) {
867
893
  const fullTableName = this.getTableName(storage.TABLE_TRACES);
868
894
  try {
@@ -878,6 +904,12 @@ var D1Store = class extends storage.MastraStorage {
878
904
  query.jsonLike("attributes", key, value);
879
905
  }
880
906
  }
907
+ if (fromDate) {
908
+ query.andWhere("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
909
+ }
910
+ if (toDate) {
911
+ query.andWhere("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
912
+ }
881
913
  query.orderBy("startTime", "DESC").limit(perPage).offset((page - 1) * perPage);
882
914
  const { sql, params } = query.build();
883
915
  const results = await this.executeQuery({ sql, params });
@@ -929,8 +961,107 @@ var D1Store = class extends storage.MastraStorage {
929
961
  return [];
930
962
  }
931
963
  }
932
- getWorkflowRuns(_args) {
933
- throw new Error("Method not implemented.");
964
+ parseWorkflowRun(row) {
965
+ let parsedSnapshot = row.snapshot;
966
+ if (typeof parsedSnapshot === "string") {
967
+ try {
968
+ parsedSnapshot = JSON.parse(row.snapshot);
969
+ } catch (e) {
970
+ console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
971
+ }
972
+ }
973
+ return {
974
+ workflowName: row.workflow_name,
975
+ runId: row.run_id,
976
+ snapshot: parsedSnapshot,
977
+ createdAt: this.ensureDate(row.createdAt),
978
+ updatedAt: this.ensureDate(row.updatedAt),
979
+ resourceId: row.resourceId
980
+ };
981
+ }
982
+ async hasColumn(table, column) {
983
+ const sql = `PRAGMA table_info(${table});`;
984
+ const result = await this.executeQuery({ sql, params: [] });
985
+ if (!result || !Array.isArray(result)) return false;
986
+ return result.some((col) => col.name === column || col.name === column.toLowerCase());
987
+ }
988
+ async getWorkflowRuns({
989
+ workflowName,
990
+ fromDate,
991
+ toDate,
992
+ limit,
993
+ offset,
994
+ resourceId
995
+ } = {}) {
996
+ const fullTableName = this.getTableName(storage.TABLE_WORKFLOW_SNAPSHOT);
997
+ try {
998
+ const builder = createSqlBuilder().select().from(fullTableName);
999
+ const countBuilder = createSqlBuilder().count().from(fullTableName);
1000
+ if (workflowName) builder.whereAnd("workflow_name = ?", workflowName);
1001
+ if (resourceId) {
1002
+ const hasResourceId = await this.hasColumn(fullTableName, "resourceId");
1003
+ if (hasResourceId) {
1004
+ builder.whereAnd("resourceId = ?", resourceId);
1005
+ countBuilder.whereAnd("resourceId = ?", resourceId);
1006
+ } else {
1007
+ console.warn(`[${fullTableName}] resourceId column not found. Skipping resourceId filter.`);
1008
+ }
1009
+ }
1010
+ if (fromDate) {
1011
+ builder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
1012
+ countBuilder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
1013
+ }
1014
+ if (toDate) {
1015
+ builder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
1016
+ countBuilder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
1017
+ }
1018
+ builder.orderBy("createdAt", "DESC");
1019
+ if (typeof limit === "number") builder.limit(limit);
1020
+ if (typeof offset === "number") builder.offset(offset);
1021
+ const { sql, params } = builder.build();
1022
+ let total = 0;
1023
+ if (limit !== void 0 && offset !== void 0) {
1024
+ const { sql: countSql, params: countParams } = countBuilder.build();
1025
+ const countResult = await this.executeQuery({ sql: countSql, params: countParams, first: true });
1026
+ total = Number(countResult?.count ?? 0);
1027
+ }
1028
+ const results = await this.executeQuery({ sql, params });
1029
+ const runs = (isArrayOfRecords(results) ? results : []).map((row) => this.parseWorkflowRun(row));
1030
+ return { runs, total: total || runs.length };
1031
+ } catch (error) {
1032
+ this.logger.error("Error getting workflow runs:", {
1033
+ message: error instanceof Error ? error.message : String(error)
1034
+ });
1035
+ throw error;
1036
+ }
1037
+ }
1038
+ async getWorkflowRunById({
1039
+ runId,
1040
+ workflowName
1041
+ }) {
1042
+ const fullTableName = this.getTableName(storage.TABLE_WORKFLOW_SNAPSHOT);
1043
+ try {
1044
+ const conditions = [];
1045
+ const params = [];
1046
+ if (runId) {
1047
+ conditions.push("run_id = ?");
1048
+ params.push(runId);
1049
+ }
1050
+ if (workflowName) {
1051
+ conditions.push("workflow_name = ?");
1052
+ params.push(workflowName);
1053
+ }
1054
+ const whereClause = conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : "";
1055
+ const sql = `SELECT * FROM ${fullTableName} ${whereClause} ORDER BY createdAt DESC LIMIT 1`;
1056
+ const result = await this.executeQuery({ sql, params, first: true });
1057
+ if (!result) return null;
1058
+ return this.parseWorkflowRun(result);
1059
+ } catch (error) {
1060
+ this.logger.error("Error getting workflow run by ID:", {
1061
+ message: error instanceof Error ? error.message : String(error)
1062
+ });
1063
+ throw error;
1064
+ }
934
1065
  }
935
1066
  /**
936
1067
  * Close the database connection
package/dist/index.d.cts CHANGED
@@ -1,4 +1,3 @@
1
- export { SqlQueryOptions } from './_tsup-dts-rollup.cjs';
2
1
  export { D1Config } from './_tsup-dts-rollup.cjs';
3
2
  export { D1WorkersConfig } from './_tsup-dts-rollup.cjs';
4
3
  export { D1StoreConfig } from './_tsup-dts-rollup.cjs';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- export { SqlQueryOptions } from './_tsup-dts-rollup.js';
2
1
  export { D1Config } from './_tsup-dts-rollup.js';
3
2
  export { D1WorkersConfig } from './_tsup-dts-rollup.js';
4
3
  export { D1StoreConfig } from './_tsup-dts-rollup.js';
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
+ import { MessageList } from '@mastra/core/agent';
1
2
  import { MastraStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_TRACES, TABLE_EVALS } from '@mastra/core/storage';
2
3
  import Cloudflare from 'cloudflare';
4
+ import { parseSqlIdentifier } from '@mastra/core/utils';
3
5
 
4
6
  // src/storage/index.ts
5
-
6
- // src/storage/sql-builder.ts
7
7
  var SqlBuilder = class {
8
8
  sql = "";
9
9
  params = [];
@@ -13,12 +13,15 @@ var SqlBuilder = class {
13
13
  if (!columns || Array.isArray(columns) && columns.length === 0) {
14
14
  this.sql = "SELECT *";
15
15
  } else {
16
- this.sql = `SELECT ${Array.isArray(columns) ? columns.join(", ") : columns}`;
16
+ const cols = Array.isArray(columns) ? columns : [columns];
17
+ const parsedCols = cols.map((col) => parseSelectIdentifier(col));
18
+ this.sql = `SELECT ${parsedCols.join(", ")}`;
17
19
  }
18
20
  return this;
19
21
  }
20
22
  from(table) {
21
- this.sql += ` FROM ${table}`;
23
+ const parsedTableName = parseSqlIdentifier(table, "table name");
24
+ this.sql += ` FROM ${parsedTableName}`;
22
25
  return this;
23
26
  }
24
27
  /**
@@ -55,7 +58,11 @@ var SqlBuilder = class {
55
58
  return this;
56
59
  }
57
60
  orderBy(column, direction = "ASC") {
58
- this.sql += ` ORDER BY ${column} ${direction}`;
61
+ const parsedColumn = parseSqlIdentifier(column, "column name");
62
+ if (!["ASC", "DESC"].includes(direction)) {
63
+ throw new Error(`Invalid sort direction: ${direction}`);
64
+ }
65
+ this.sql += ` ORDER BY ${parsedColumn} ${direction}`;
59
66
  return this;
60
67
  }
61
68
  limit(count) {
@@ -68,6 +75,10 @@ var SqlBuilder = class {
68
75
  this.params.push(count);
69
76
  return this;
70
77
  }
78
+ count() {
79
+ this.sql += "SELECT COUNT(*) AS count";
80
+ return this;
81
+ }
71
82
  /**
72
83
  * Insert a row, or update specific columns on conflict (upsert).
73
84
  * @param table Table name
@@ -77,27 +88,33 @@ var SqlBuilder = class {
77
88
  * @param updateMap Object mapping columns to update to their new value (e.g. { name: 'excluded.name' })
78
89
  */
79
90
  insert(table, columns, values, conflictColumns, updateMap) {
80
- const placeholders = columns.map(() => "?").join(", ");
91
+ const parsedTableName = parseSqlIdentifier(table, "table name");
92
+ const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
93
+ const placeholders = parsedColumns.map(() => "?").join(", ");
81
94
  if (conflictColumns && updateMap) {
95
+ const parsedConflictColumns = conflictColumns.map((col) => parseSqlIdentifier(col, "column name"));
82
96
  const updateClause = Object.entries(updateMap).map(([col, expr]) => `${col} = ${expr}`).join(", ");
83
- this.sql = `INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${conflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
97
+ this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${parsedConflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
84
98
  this.params.push(...values);
85
99
  return this;
86
100
  }
87
- this.sql = `INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders})`;
101
+ this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders})`;
88
102
  this.params.push(...values);
89
103
  return this;
90
104
  }
91
105
  // Update operations
92
106
  update(table, columns, values) {
93
- const setClause = columns.map((col) => `${col} = ?`).join(", ");
94
- this.sql = `UPDATE ${table} SET ${setClause}`;
107
+ const parsedTableName = parseSqlIdentifier(table, "table name");
108
+ const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
109
+ const setClause = parsedColumns.map((col) => `${col} = ?`).join(", ");
110
+ this.sql = `UPDATE ${parsedTableName} SET ${setClause}`;
95
111
  this.params.push(...values);
96
112
  return this;
97
113
  }
98
114
  // Delete operations
99
115
  delete(table) {
100
- this.sql = `DELETE FROM ${table}`;
116
+ const parsedTableName = parseSqlIdentifier(table, "table name");
117
+ this.sql = `DELETE FROM ${parsedTableName}`;
101
118
  return this;
102
119
  }
103
120
  /**
@@ -108,9 +125,16 @@ var SqlBuilder = class {
108
125
  * @returns The builder instance
109
126
  */
110
127
  createTable(table, columnDefinitions, tableConstraints) {
111
- const columns = columnDefinitions.join(", ");
128
+ const parsedTableName = parseSqlIdentifier(table, "table name");
129
+ const parsedColumnDefinitions = columnDefinitions.map((def) => {
130
+ const colName = def.split(/\s+/)[0];
131
+ if (!colName) throw new Error("Empty column name in definition");
132
+ parseSqlIdentifier(colName, "column name");
133
+ return def;
134
+ });
135
+ const columns = parsedColumnDefinitions.join(", ");
112
136
  const constraints = tableConstraints && tableConstraints.length > 0 ? ", " + tableConstraints.join(", ") : "";
113
- this.sql = `CREATE TABLE IF NOT EXISTS ${table} (${columns}${constraints})`;
137
+ this.sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns}${constraints})`;
114
138
  return this;
115
139
  }
116
140
  /**
@@ -133,13 +157,10 @@ var SqlBuilder = class {
133
157
  * @returns The builder instance
134
158
  */
135
159
  createIndex(indexName, tableName, columnName, indexType = "") {
136
- this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${columnName})`;
137
- return this;
138
- }
139
- // Raw SQL with params
140
- raw(sql, ...params) {
141
- this.sql = sql;
142
- this.params.push(...params);
160
+ const parsedIndexName = parseSqlIdentifier(indexName, "index name");
161
+ const parsedTableName = parseSqlIdentifier(tableName, "table name");
162
+ const parsedColumnName = parseSqlIdentifier(columnName, "column name");
163
+ this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${parsedIndexName} ON ${parsedTableName}(${parsedColumnName})`;
143
164
  return this;
144
165
  }
145
166
  /**
@@ -149,11 +170,12 @@ var SqlBuilder = class {
149
170
  * @param exact If true, will not add % wildcards
150
171
  */
151
172
  like(column, value, exact = false) {
173
+ const parsedColumnName = parseSqlIdentifier(column, "column name");
152
174
  const likeValue = exact ? value : `%${value}%`;
153
175
  if (this.whereAdded) {
154
- this.sql += ` AND ${column} LIKE ?`;
176
+ this.sql += ` AND ${parsedColumnName} LIKE ?`;
155
177
  } else {
156
- this.sql += ` WHERE ${column} LIKE ?`;
178
+ this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
157
179
  this.whereAdded = true;
158
180
  }
159
181
  this.params.push(likeValue);
@@ -166,11 +188,13 @@ var SqlBuilder = class {
166
188
  * @param value The value to match
167
189
  */
168
190
  jsonLike(column, key, value) {
169
- const jsonPattern = `%"${key}":"${value}"%`;
191
+ const parsedColumnName = parseSqlIdentifier(column, "column name");
192
+ const parsedKey = parseSqlIdentifier(key, "key name");
193
+ const jsonPattern = `%"${parsedKey}":"${value}"%`;
170
194
  if (this.whereAdded) {
171
- this.sql += ` AND ${column} LIKE ?`;
195
+ this.sql += ` AND ${parsedColumnName} LIKE ?`;
172
196
  } else {
173
- this.sql += ` WHERE ${column} LIKE ?`;
197
+ this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
174
198
  this.whereAdded = true;
175
199
  }
176
200
  this.params.push(jsonPattern);
@@ -200,6 +224,15 @@ var SqlBuilder = class {
200
224
  function createSqlBuilder() {
201
225
  return new SqlBuilder();
202
226
  }
227
+ var SQL_IDENTIFIER_PATTERN = /^[a-zA-Z0-9_]+(\s+AS\s+[a-zA-Z0-9_]+)?$/;
228
+ function parseSelectIdentifier(column) {
229
+ if (column !== "*" && !SQL_IDENTIFIER_PATTERN.test(column)) {
230
+ throw new Error(
231
+ `Invalid column name: "${column}". Must be "*" or a valid identifier (letters, numbers, underscores), optionally with "AS alias".`
232
+ );
233
+ }
234
+ return column;
235
+ }
203
236
 
204
237
  // src/storage/index.ts
205
238
  function isArrayOfRecords(value) {
@@ -218,6 +251,9 @@ var D1Store = class extends MastraStorage {
218
251
  */
219
252
  constructor(config) {
220
253
  super({ name: "D1" });
254
+ if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) {
255
+ throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
256
+ }
221
257
  this.tablePrefix = config.tablePrefix || "";
222
258
  if ("binding" in config) {
223
259
  if (!config.binding) {
@@ -244,30 +280,6 @@ var D1Store = class extends MastraStorage {
244
280
  formatSqlParams(params) {
245
281
  return params.map((p) => p === void 0 || p === null ? null : p);
246
282
  }
247
- // Helper method to create SQL indexes for better query performance
248
- async createIndexIfNotExists(tableName, columnName, indexType = "") {
249
- const fullTableName = this.getTableName(tableName);
250
- const indexName = `idx_${tableName}_${columnName}`;
251
- try {
252
- const checkQuery = createSqlBuilder().checkIndexExists(indexName, fullTableName);
253
- const { sql: checkSql, params: checkParams } = checkQuery.build();
254
- const indexExists = await this.executeQuery({
255
- sql: checkSql,
256
- params: checkParams,
257
- first: true
258
- });
259
- if (!indexExists) {
260
- const createQuery = createSqlBuilder().createIndex(indexName, fullTableName, columnName, indexType);
261
- const { sql: createSql, params: createParams } = createQuery.build();
262
- await this.executeQuery({ sql: createSql, params: createParams });
263
- this.logger.debug(`Created index ${indexName} on ${fullTableName}(${columnName})`);
264
- }
265
- } catch (error) {
266
- this.logger.error(`Error creating index on ${fullTableName}(${columnName}):`, {
267
- message: error instanceof Error ? error.message : String(error)
268
- });
269
- }
270
- }
271
283
  async executeWorkersBindingQuery({
272
284
  sql,
273
285
  params = [],
@@ -490,7 +502,8 @@ var D1Store = class extends MastraStorage {
490
502
  try {
491
503
  await this.executeQuery({ sql, params });
492
504
  } catch (error) {
493
- this.logger.error(`Error inserting into ${fullTableName}:`, { error });
505
+ const message = error instanceof Error ? error.message : String(error);
506
+ this.logger.error(`Error inserting into ${fullTableName}:`, { message });
494
507
  throw new Error(`Failed to insert into ${fullTableName}: ${error}`);
495
508
  }
496
509
  }
@@ -588,7 +601,8 @@ var D1Store = class extends MastraStorage {
588
601
  await this.executeQuery({ sql, params });
589
602
  return thread;
590
603
  } catch (error) {
591
- this.logger.error(`Error saving thread to ${fullTableName}:`, { error });
604
+ const message = error instanceof Error ? error.message : String(error);
605
+ this.logger.error(`Error saving thread to ${fullTableName}:`, { message });
592
606
  throw error;
593
607
  }
594
608
  }
@@ -622,7 +636,8 @@ var D1Store = class extends MastraStorage {
622
636
  updatedAt: /* @__PURE__ */ new Date()
623
637
  };
624
638
  } catch (error) {
625
- this.logger.error("Error updating thread:", { error });
639
+ const message = error instanceof Error ? error.message : String(error);
640
+ this.logger.error("Error updating thread:", { message });
626
641
  throw error;
627
642
  }
628
643
  }
@@ -643,8 +658,8 @@ var D1Store = class extends MastraStorage {
643
658
  throw new Error(`Failed to delete thread ${threadId}: ${error}`);
644
659
  }
645
660
  }
646
- // Thread and message management methods
647
- async saveMessages({ messages }) {
661
+ async saveMessages(args) {
662
+ const { messages, format = "v1" } = args;
648
663
  if (messages.length === 0) return [];
649
664
  try {
650
665
  const now = /* @__PURE__ */ new Date();
@@ -666,7 +681,7 @@ var D1Store = class extends MastraStorage {
666
681
  content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
667
682
  createdAt: createdAt.toISOString(),
668
683
  role: message.role,
669
- type: message.type
684
+ type: message.type || "v2"
670
685
  };
671
686
  });
672
687
  await this.batchInsert({
@@ -674,13 +689,19 @@ var D1Store = class extends MastraStorage {
674
689
  records: messagesToInsert
675
690
  });
676
691
  this.logger.debug(`Saved ${messages.length} messages`);
677
- return messages;
692
+ const list = new MessageList().add(messages, "memory");
693
+ if (format === `v2`) return list.get.all.v2();
694
+ return list.get.all.v1();
678
695
  } catch (error) {
679
696
  this.logger.error("Error saving messages:", { message: error instanceof Error ? error.message : String(error) });
680
697
  throw error;
681
698
  }
682
699
  }
683
- async getMessages({ threadId, selectBy }) {
700
+ async getMessages({
701
+ threadId,
702
+ selectBy,
703
+ format
704
+ }) {
684
705
  const fullTableName = this.getTableName(TABLE_MESSAGES);
685
706
  const limit = typeof selectBy?.last === "number" ? selectBy.last : 40;
686
707
  const include = selectBy?.include || [];
@@ -704,7 +725,7 @@ var D1Store = class extends MastraStorage {
704
725
  m.role,
705
726
  m.type,
706
727
  m.createdAt,
707
- m.thread_id AS "threadId"
728
+ m.thread_id AS threadId
708
729
  FROM ordered_messages m
709
730
  WHERE m.id IN (${includeIds.map(() => "?").join(",")})
710
731
  OR EXISTS (
@@ -731,7 +752,7 @@ var D1Store = class extends MastraStorage {
731
752
  if (Array.isArray(includeResult)) messages.push(...includeResult);
732
753
  }
733
754
  const excludeIds = messages.map((m) => m.id);
734
- let query = createSqlBuilder().select(["id", "content", "role", "type", '"createdAt"', 'thread_id AS "threadId"']).from(fullTableName).where("thread_id = ?", threadId).andWhere(`id NOT IN (${excludeIds.map(() => "?").join(",")})`, ...excludeIds).orderBy("createdAt", "DESC").limit(limit);
755
+ let query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId"]).from(fullTableName).where("thread_id = ?", threadId).andWhere(`id NOT IN (${excludeIds.map(() => "?").join(",")})`, ...excludeIds).orderBy("createdAt", "DESC").limit(limit);
735
756
  const { sql, params } = query.build();
736
757
  const result = await this.executeQuery({ sql, params });
737
758
  if (Array.isArray(result)) messages.push(...result);
@@ -745,12 +766,15 @@ var D1Store = class extends MastraStorage {
745
766
  const processedMessages = messages.map((message) => {
746
767
  const processedMsg = {};
747
768
  for (const [key, value] of Object.entries(message)) {
769
+ if (key === `type` && value === `v2`) continue;
748
770
  processedMsg[key] = this.deserializeValue(value);
749
771
  }
750
772
  return processedMsg;
751
773
  });
752
774
  this.logger.debug(`Retrieved ${messages.length} messages for thread ${threadId}`);
753
- return processedMessages;
775
+ const list = new MessageList().add(processedMessages, "memory");
776
+ if (format === `v2`) return list.get.all.v2();
777
+ return list.get.all.v1();
754
778
  } catch (error) {
755
779
  this.logger.error("Error retrieving messages for thread", {
756
780
  threadId,
@@ -856,7 +880,9 @@ var D1Store = class extends MastraStorage {
856
880
  scope,
857
881
  page,
858
882
  perPage,
859
- attributes
883
+ attributes,
884
+ fromDate,
885
+ toDate
860
886
  }) {
861
887
  const fullTableName = this.getTableName(TABLE_TRACES);
862
888
  try {
@@ -872,6 +898,12 @@ var D1Store = class extends MastraStorage {
872
898
  query.jsonLike("attributes", key, value);
873
899
  }
874
900
  }
901
+ if (fromDate) {
902
+ query.andWhere("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
903
+ }
904
+ if (toDate) {
905
+ query.andWhere("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
906
+ }
875
907
  query.orderBy("startTime", "DESC").limit(perPage).offset((page - 1) * perPage);
876
908
  const { sql, params } = query.build();
877
909
  const results = await this.executeQuery({ sql, params });
@@ -923,8 +955,107 @@ var D1Store = class extends MastraStorage {
923
955
  return [];
924
956
  }
925
957
  }
926
- getWorkflowRuns(_args) {
927
- throw new Error("Method not implemented.");
958
+ parseWorkflowRun(row) {
959
+ let parsedSnapshot = row.snapshot;
960
+ if (typeof parsedSnapshot === "string") {
961
+ try {
962
+ parsedSnapshot = JSON.parse(row.snapshot);
963
+ } catch (e) {
964
+ console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
965
+ }
966
+ }
967
+ return {
968
+ workflowName: row.workflow_name,
969
+ runId: row.run_id,
970
+ snapshot: parsedSnapshot,
971
+ createdAt: this.ensureDate(row.createdAt),
972
+ updatedAt: this.ensureDate(row.updatedAt),
973
+ resourceId: row.resourceId
974
+ };
975
+ }
976
+ async hasColumn(table, column) {
977
+ const sql = `PRAGMA table_info(${table});`;
978
+ const result = await this.executeQuery({ sql, params: [] });
979
+ if (!result || !Array.isArray(result)) return false;
980
+ return result.some((col) => col.name === column || col.name === column.toLowerCase());
981
+ }
982
+ async getWorkflowRuns({
983
+ workflowName,
984
+ fromDate,
985
+ toDate,
986
+ limit,
987
+ offset,
988
+ resourceId
989
+ } = {}) {
990
+ const fullTableName = this.getTableName(TABLE_WORKFLOW_SNAPSHOT);
991
+ try {
992
+ const builder = createSqlBuilder().select().from(fullTableName);
993
+ const countBuilder = createSqlBuilder().count().from(fullTableName);
994
+ if (workflowName) builder.whereAnd("workflow_name = ?", workflowName);
995
+ if (resourceId) {
996
+ const hasResourceId = await this.hasColumn(fullTableName, "resourceId");
997
+ if (hasResourceId) {
998
+ builder.whereAnd("resourceId = ?", resourceId);
999
+ countBuilder.whereAnd("resourceId = ?", resourceId);
1000
+ } else {
1001
+ console.warn(`[${fullTableName}] resourceId column not found. Skipping resourceId filter.`);
1002
+ }
1003
+ }
1004
+ if (fromDate) {
1005
+ builder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
1006
+ countBuilder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
1007
+ }
1008
+ if (toDate) {
1009
+ builder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
1010
+ countBuilder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
1011
+ }
1012
+ builder.orderBy("createdAt", "DESC");
1013
+ if (typeof limit === "number") builder.limit(limit);
1014
+ if (typeof offset === "number") builder.offset(offset);
1015
+ const { sql, params } = builder.build();
1016
+ let total = 0;
1017
+ if (limit !== void 0 && offset !== void 0) {
1018
+ const { sql: countSql, params: countParams } = countBuilder.build();
1019
+ const countResult = await this.executeQuery({ sql: countSql, params: countParams, first: true });
1020
+ total = Number(countResult?.count ?? 0);
1021
+ }
1022
+ const results = await this.executeQuery({ sql, params });
1023
+ const runs = (isArrayOfRecords(results) ? results : []).map((row) => this.parseWorkflowRun(row));
1024
+ return { runs, total: total || runs.length };
1025
+ } catch (error) {
1026
+ this.logger.error("Error getting workflow runs:", {
1027
+ message: error instanceof Error ? error.message : String(error)
1028
+ });
1029
+ throw error;
1030
+ }
1031
+ }
1032
+ async getWorkflowRunById({
1033
+ runId,
1034
+ workflowName
1035
+ }) {
1036
+ const fullTableName = this.getTableName(TABLE_WORKFLOW_SNAPSHOT);
1037
+ try {
1038
+ const conditions = [];
1039
+ const params = [];
1040
+ if (runId) {
1041
+ conditions.push("run_id = ?");
1042
+ params.push(runId);
1043
+ }
1044
+ if (workflowName) {
1045
+ conditions.push("workflow_name = ?");
1046
+ params.push(workflowName);
1047
+ }
1048
+ const whereClause = conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : "";
1049
+ const sql = `SELECT * FROM ${fullTableName} ${whereClause} ORDER BY createdAt DESC LIMIT 1`;
1050
+ const result = await this.executeQuery({ sql, params, first: true });
1051
+ if (!result) return null;
1052
+ return this.parseWorkflowRun(result);
1053
+ } catch (error) {
1054
+ this.logger.error("Error getting workflow run by ID:", {
1055
+ message: error instanceof Error ? error.message : String(error)
1056
+ });
1057
+ throw error;
1058
+ }
928
1059
  }
929
1060
  /**
930
1061
  * Close the database connection
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/cloudflare-d1",
3
- "version": "0.0.0-switch-to-core-20250424015131",
3
+ "version": "0.0.0-taofeeqInngest-20250603090617",
4
4
  "description": "D1 provider for Mastra - includes db storage capabilities",
5
5
  "type": "module",
6
6
  "files": [
@@ -22,20 +22,23 @@
22
22
  "./package.json": "./package.json"
23
23
  },
24
24
  "dependencies": {
25
- "cloudflare": "^4.1.0",
26
- "@mastra/core": "0.0.0-switch-to-core-20250424015131"
25
+ "cloudflare": "^4.1.0"
27
26
  },
28
27
  "devDependencies": {
29
28
  "@cloudflare/workers-types": "^4.20250417.0",
30
- "@microsoft/api-extractor": "^7.52.1",
29
+ "@microsoft/api-extractor": "^7.52.5",
31
30
  "@types/node": "^20.17.27",
32
31
  "dotenv": "^16.4.7",
33
32
  "eslint": "^9.23.0",
34
33
  "miniflare": "^4.20250410.1",
35
34
  "tsup": "^8.4.0",
36
35
  "typescript": "^5.8.2",
37
- "vitest": "^3.0.9",
38
- "@internal/lint": "0.0.2"
36
+ "vitest": "^3.1.2",
37
+ "@mastra/core": "0.0.0-taofeeqInngest-20250603090617",
38
+ "@internal/lint": "0.0.0-taofeeqInngest-20250603090617"
39
+ },
40
+ "peerDependencies": {
41
+ "@mastra/core": "^0.10.0-alpha.0"
39
42
  },
40
43
  "scripts": {
41
44
  "build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",