@mastra/cloudflare-d1 0.1.9 → 0.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.
@@ -213,9 +213,30 @@ declare interface D1WorkersConfig {
213
213
  export { D1WorkersConfig }
214
214
  export { D1WorkersConfig as D1WorkersConfig_alias_1 }
215
215
 
216
+ /**
217
+ * Parses and returns a valid SQL SELECT column identifier.
218
+ * Allows a single identifier (letters, numbers, underscores), or '*', optionally with 'AS alias'.
219
+ *
220
+ * @param column - The column identifier string to parse.
221
+ * @returns The validated column identifier as a branded type.
222
+ * @throws {Error} If invalid.
223
+ *
224
+ * @example
225
+ * const col = parseSelectIdentifier('user_id'); // Ok
226
+ * parseSelectIdentifier('user_id AS uid'); // Ok
227
+ * parseSelectIdentifier('*'); // Ok
228
+ * parseSelectIdentifier('user id'); // Throws error
229
+ */
230
+ export declare function parseSelectIdentifier(column: string): SelectIdentifier;
231
+
216
232
  export declare const retryUntil: <T>(fn: () => Promise<T>, condition: (result: T) => boolean, timeout?: number, // REST API needs longer timeout due to higher latency
217
233
  interval?: number) => Promise<T>;
218
234
 
235
+ /** Represents a validated SQL SELECT column identifier (or '*', optionally with 'AS alias'). */
236
+ declare type SelectIdentifier = string & {
237
+ __brand: 'SelectIdentifier';
238
+ };
239
+
219
240
  /**
220
241
  * SQL Builder class for constructing type-safe SQL queries
221
242
  * This helps create maintainable and secure SQL queries with proper parameter handling
@@ -279,7 +300,6 @@ export declare class SqlBuilder {
279
300
  * @returns The builder instance
280
301
  */
281
302
  createIndex(indexName: string, tableName: string, columnName: string, indexType?: string): SqlBuilder;
282
- raw(sql: string, ...params: SqlParam[]): SqlBuilder;
283
303
  /**
284
304
  * Add a LIKE condition to the query
285
305
  * @param column The column to check
@@ -317,7 +337,7 @@ export declare type SqlParam = string | number | boolean | null | undefined;
317
337
  /**
318
338
  * Interface for SQL query options with generic type support
319
339
  */
320
- declare interface SqlQueryOptions {
340
+ export declare interface SqlQueryOptions {
321
341
  /** SQL query to execute */
322
342
  sql: string;
323
343
  /** Parameters to bind to the query */
@@ -325,7 +345,5 @@ declare interface SqlQueryOptions {
325
345
  /** Whether to return only the first result */
326
346
  first?: boolean;
327
347
  }
328
- export { SqlQueryOptions }
329
- export { SqlQueryOptions as SqlQueryOptions_alias_1 }
330
348
 
331
349
  export { }
@@ -213,9 +213,30 @@ declare interface D1WorkersConfig {
213
213
  export { D1WorkersConfig }
214
214
  export { D1WorkersConfig as D1WorkersConfig_alias_1 }
215
215
 
216
+ /**
217
+ * Parses and returns a valid SQL SELECT column identifier.
218
+ * Allows a single identifier (letters, numbers, underscores), or '*', optionally with 'AS alias'.
219
+ *
220
+ * @param column - The column identifier string to parse.
221
+ * @returns The validated column identifier as a branded type.
222
+ * @throws {Error} If invalid.
223
+ *
224
+ * @example
225
+ * const col = parseSelectIdentifier('user_id'); // Ok
226
+ * parseSelectIdentifier('user_id AS uid'); // Ok
227
+ * parseSelectIdentifier('*'); // Ok
228
+ * parseSelectIdentifier('user id'); // Throws error
229
+ */
230
+ export declare function parseSelectIdentifier(column: string): SelectIdentifier;
231
+
216
232
  export declare const retryUntil: <T>(fn: () => Promise<T>, condition: (result: T) => boolean, timeout?: number, // REST API needs longer timeout due to higher latency
217
233
  interval?: number) => Promise<T>;
218
234
 
235
+ /** Represents a validated SQL SELECT column identifier (or '*', optionally with 'AS alias'). */
236
+ declare type SelectIdentifier = string & {
237
+ __brand: 'SelectIdentifier';
238
+ };
239
+
219
240
  /**
220
241
  * SQL Builder class for constructing type-safe SQL queries
221
242
  * This helps create maintainable and secure SQL queries with proper parameter handling
@@ -279,7 +300,6 @@ export declare class SqlBuilder {
279
300
  * @returns The builder instance
280
301
  */
281
302
  createIndex(indexName: string, tableName: string, columnName: string, indexType?: string): SqlBuilder;
282
- raw(sql: string, ...params: SqlParam[]): SqlBuilder;
283
303
  /**
284
304
  * Add a LIKE condition to the query
285
305
  * @param column The column to check
@@ -317,7 +337,7 @@ export declare type SqlParam = string | number | boolean | null | undefined;
317
337
  /**
318
338
  * Interface for SQL query options with generic type support
319
339
  */
320
- declare interface SqlQueryOptions {
340
+ export declare interface SqlQueryOptions {
321
341
  /** SQL query to execute */
322
342
  sql: string;
323
343
  /** Parameters to bind to the query */
@@ -325,7 +345,5 @@ declare interface SqlQueryOptions {
325
345
  /** Whether to return only the first result */
326
346
  first?: boolean;
327
347
  }
328
- export { SqlQueryOptions }
329
- export { SqlQueryOptions as SqlQueryOptions_alias_1 }
330
348
 
331
349
  export { }
package/dist/index.cjs CHANGED
@@ -2,14 +2,13 @@
2
2
 
3
3
  var storage = require('@mastra/core/storage');
4
4
  var Cloudflare = require('cloudflare');
5
+ var utils = require('@mastra/core/utils');
5
6
 
6
7
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
8
 
8
9
  var Cloudflare__default = /*#__PURE__*/_interopDefault(Cloudflare);
9
10
 
10
11
  // src/storage/index.ts
11
-
12
- // src/storage/sql-builder.ts
13
12
  var SqlBuilder = class {
14
13
  sql = "";
15
14
  params = [];
@@ -19,12 +18,15 @@ var SqlBuilder = class {
19
18
  if (!columns || Array.isArray(columns) && columns.length === 0) {
20
19
  this.sql = "SELECT *";
21
20
  } else {
22
- this.sql = `SELECT ${Array.isArray(columns) ? columns.join(", ") : columns}`;
21
+ const cols = Array.isArray(columns) ? columns : [columns];
22
+ const parsedCols = cols.map((col) => parseSelectIdentifier(col));
23
+ this.sql = `SELECT ${parsedCols.join(", ")}`;
23
24
  }
24
25
  return this;
25
26
  }
26
27
  from(table) {
27
- this.sql += ` FROM ${table}`;
28
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
29
+ this.sql += ` FROM ${parsedTableName}`;
28
30
  return this;
29
31
  }
30
32
  /**
@@ -61,7 +63,11 @@ var SqlBuilder = class {
61
63
  return this;
62
64
  }
63
65
  orderBy(column, direction = "ASC") {
64
- this.sql += ` ORDER BY ${column} ${direction}`;
66
+ const parsedColumn = utils.parseSqlIdentifier(column, "column name");
67
+ if (!["ASC", "DESC"].includes(direction)) {
68
+ throw new Error(`Invalid sort direction: ${direction}`);
69
+ }
70
+ this.sql += ` ORDER BY ${parsedColumn} ${direction}`;
65
71
  return this;
66
72
  }
67
73
  limit(count) {
@@ -87,27 +93,33 @@ var SqlBuilder = class {
87
93
  * @param updateMap Object mapping columns to update to their new value (e.g. { name: 'excluded.name' })
88
94
  */
89
95
  insert(table, columns, values, conflictColumns, updateMap) {
90
- const placeholders = columns.map(() => "?").join(", ");
96
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
97
+ const parsedColumns = columns.map((col) => utils.parseSqlIdentifier(col, "column name"));
98
+ const placeholders = parsedColumns.map(() => "?").join(", ");
91
99
  if (conflictColumns && updateMap) {
100
+ const parsedConflictColumns = conflictColumns.map((col) => utils.parseSqlIdentifier(col, "column name"));
92
101
  const updateClause = Object.entries(updateMap).map(([col, expr]) => `${col} = ${expr}`).join(", ");
93
- this.sql = `INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${conflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
102
+ this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${parsedConflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
94
103
  this.params.push(...values);
95
104
  return this;
96
105
  }
97
- this.sql = `INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders})`;
106
+ this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders})`;
98
107
  this.params.push(...values);
99
108
  return this;
100
109
  }
101
110
  // Update operations
102
111
  update(table, columns, values) {
103
- const setClause = columns.map((col) => `${col} = ?`).join(", ");
104
- this.sql = `UPDATE ${table} SET ${setClause}`;
112
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
113
+ const parsedColumns = columns.map((col) => utils.parseSqlIdentifier(col, "column name"));
114
+ const setClause = parsedColumns.map((col) => `${col} = ?`).join(", ");
115
+ this.sql = `UPDATE ${parsedTableName} SET ${setClause}`;
105
116
  this.params.push(...values);
106
117
  return this;
107
118
  }
108
119
  // Delete operations
109
120
  delete(table) {
110
- this.sql = `DELETE FROM ${table}`;
121
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
122
+ this.sql = `DELETE FROM ${parsedTableName}`;
111
123
  return this;
112
124
  }
113
125
  /**
@@ -118,9 +130,16 @@ var SqlBuilder = class {
118
130
  * @returns The builder instance
119
131
  */
120
132
  createTable(table, columnDefinitions, tableConstraints) {
121
- const columns = columnDefinitions.join(", ");
133
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
134
+ const parsedColumnDefinitions = columnDefinitions.map((def) => {
135
+ const colName = def.split(/\s+/)[0];
136
+ if (!colName) throw new Error("Empty column name in definition");
137
+ utils.parseSqlIdentifier(colName, "column name");
138
+ return def;
139
+ });
140
+ const columns = parsedColumnDefinitions.join(", ");
122
141
  const constraints = tableConstraints && tableConstraints.length > 0 ? ", " + tableConstraints.join(", ") : "";
123
- this.sql = `CREATE TABLE IF NOT EXISTS ${table} (${columns}${constraints})`;
142
+ this.sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns}${constraints})`;
124
143
  return this;
125
144
  }
126
145
  /**
@@ -143,13 +162,10 @@ var SqlBuilder = class {
143
162
  * @returns The builder instance
144
163
  */
145
164
  createIndex(indexName, tableName, columnName, indexType = "") {
146
- this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${columnName})`;
147
- return this;
148
- }
149
- // Raw SQL with params
150
- raw(sql, ...params) {
151
- this.sql = sql;
152
- this.params.push(...params);
165
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
166
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
167
+ const parsedColumnName = utils.parseSqlIdentifier(columnName, "column name");
168
+ this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${parsedIndexName} ON ${parsedTableName}(${parsedColumnName})`;
153
169
  return this;
154
170
  }
155
171
  /**
@@ -159,11 +175,12 @@ var SqlBuilder = class {
159
175
  * @param exact If true, will not add % wildcards
160
176
  */
161
177
  like(column, value, exact = false) {
178
+ const parsedColumnName = utils.parseSqlIdentifier(column, "column name");
162
179
  const likeValue = exact ? value : `%${value}%`;
163
180
  if (this.whereAdded) {
164
- this.sql += ` AND ${column} LIKE ?`;
181
+ this.sql += ` AND ${parsedColumnName} LIKE ?`;
165
182
  } else {
166
- this.sql += ` WHERE ${column} LIKE ?`;
183
+ this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
167
184
  this.whereAdded = true;
168
185
  }
169
186
  this.params.push(likeValue);
@@ -176,11 +193,13 @@ var SqlBuilder = class {
176
193
  * @param value The value to match
177
194
  */
178
195
  jsonLike(column, key, value) {
179
- const jsonPattern = `%"${key}":"${value}"%`;
196
+ const parsedColumnName = utils.parseSqlIdentifier(column, "column name");
197
+ const parsedKey = utils.parseSqlIdentifier(key, "key name");
198
+ const jsonPattern = `%"${parsedKey}":"${value}"%`;
180
199
  if (this.whereAdded) {
181
- this.sql += ` AND ${column} LIKE ?`;
200
+ this.sql += ` AND ${parsedColumnName} LIKE ?`;
182
201
  } else {
183
- this.sql += ` WHERE ${column} LIKE ?`;
202
+ this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
184
203
  this.whereAdded = true;
185
204
  }
186
205
  this.params.push(jsonPattern);
@@ -210,6 +229,15 @@ var SqlBuilder = class {
210
229
  function createSqlBuilder() {
211
230
  return new SqlBuilder();
212
231
  }
232
+ var SQL_IDENTIFIER_PATTERN = /^[a-zA-Z0-9_]+(\s+AS\s+[a-zA-Z0-9_]+)?$/;
233
+ function parseSelectIdentifier(column) {
234
+ if (column !== "*" && !SQL_IDENTIFIER_PATTERN.test(column)) {
235
+ throw new Error(
236
+ `Invalid column name: "${column}". Must be "*" or a valid identifier (letters, numbers, underscores), optionally with "AS alias".`
237
+ );
238
+ }
239
+ return column;
240
+ }
213
241
 
214
242
  // src/storage/index.ts
215
243
  function isArrayOfRecords(value) {
@@ -228,6 +256,9 @@ var D1Store = class extends storage.MastraStorage {
228
256
  */
229
257
  constructor(config) {
230
258
  super({ name: "D1" });
259
+ if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) {
260
+ throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
261
+ }
231
262
  this.tablePrefix = config.tablePrefix || "";
232
263
  if ("binding" in config) {
233
264
  if (!config.binding) {
@@ -717,7 +748,7 @@ var D1Store = class extends storage.MastraStorage {
717
748
  m.role,
718
749
  m.type,
719
750
  m.createdAt,
720
- m.thread_id AS "threadId"
751
+ m.thread_id AS threadId
721
752
  FROM ordered_messages m
722
753
  WHERE m.id IN (${includeIds.map(() => "?").join(",")})
723
754
  OR EXISTS (
@@ -744,7 +775,7 @@ var D1Store = class extends storage.MastraStorage {
744
775
  if (Array.isArray(includeResult)) messages.push(...includeResult);
745
776
  }
746
777
  const excludeIds = messages.map((m) => m.id);
747
- 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);
778
+ 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);
748
779
  const { sql, params } = query.build();
749
780
  const result = await this.executeQuery({ sql, params });
750
781
  if (Array.isArray(result)) messages.push(...result);
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,8 @@
1
1
  import { MastraStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_TRACES, TABLE_EVALS } from '@mastra/core/storage';
2
2
  import Cloudflare from 'cloudflare';
3
+ import { parseSqlIdentifier } from '@mastra/core/utils';
3
4
 
4
5
  // src/storage/index.ts
5
-
6
- // src/storage/sql-builder.ts
7
6
  var SqlBuilder = class {
8
7
  sql = "";
9
8
  params = [];
@@ -13,12 +12,15 @@ var SqlBuilder = class {
13
12
  if (!columns || Array.isArray(columns) && columns.length === 0) {
14
13
  this.sql = "SELECT *";
15
14
  } else {
16
- this.sql = `SELECT ${Array.isArray(columns) ? columns.join(", ") : columns}`;
15
+ const cols = Array.isArray(columns) ? columns : [columns];
16
+ const parsedCols = cols.map((col) => parseSelectIdentifier(col));
17
+ this.sql = `SELECT ${parsedCols.join(", ")}`;
17
18
  }
18
19
  return this;
19
20
  }
20
21
  from(table) {
21
- this.sql += ` FROM ${table}`;
22
+ const parsedTableName = parseSqlIdentifier(table, "table name");
23
+ this.sql += ` FROM ${parsedTableName}`;
22
24
  return this;
23
25
  }
24
26
  /**
@@ -55,7 +57,11 @@ var SqlBuilder = class {
55
57
  return this;
56
58
  }
57
59
  orderBy(column, direction = "ASC") {
58
- this.sql += ` ORDER BY ${column} ${direction}`;
60
+ const parsedColumn = parseSqlIdentifier(column, "column name");
61
+ if (!["ASC", "DESC"].includes(direction)) {
62
+ throw new Error(`Invalid sort direction: ${direction}`);
63
+ }
64
+ this.sql += ` ORDER BY ${parsedColumn} ${direction}`;
59
65
  return this;
60
66
  }
61
67
  limit(count) {
@@ -81,27 +87,33 @@ var SqlBuilder = class {
81
87
  * @param updateMap Object mapping columns to update to their new value (e.g. { name: 'excluded.name' })
82
88
  */
83
89
  insert(table, columns, values, conflictColumns, updateMap) {
84
- const placeholders = columns.map(() => "?").join(", ");
90
+ const parsedTableName = parseSqlIdentifier(table, "table name");
91
+ const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
92
+ const placeholders = parsedColumns.map(() => "?").join(", ");
85
93
  if (conflictColumns && updateMap) {
94
+ const parsedConflictColumns = conflictColumns.map((col) => parseSqlIdentifier(col, "column name"));
86
95
  const updateClause = Object.entries(updateMap).map(([col, expr]) => `${col} = ${expr}`).join(", ");
87
- this.sql = `INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${conflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
96
+ this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${parsedConflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
88
97
  this.params.push(...values);
89
98
  return this;
90
99
  }
91
- this.sql = `INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders})`;
100
+ this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders})`;
92
101
  this.params.push(...values);
93
102
  return this;
94
103
  }
95
104
  // Update operations
96
105
  update(table, columns, values) {
97
- const setClause = columns.map((col) => `${col} = ?`).join(", ");
98
- this.sql = `UPDATE ${table} SET ${setClause}`;
106
+ const parsedTableName = parseSqlIdentifier(table, "table name");
107
+ const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
108
+ const setClause = parsedColumns.map((col) => `${col} = ?`).join(", ");
109
+ this.sql = `UPDATE ${parsedTableName} SET ${setClause}`;
99
110
  this.params.push(...values);
100
111
  return this;
101
112
  }
102
113
  // Delete operations
103
114
  delete(table) {
104
- this.sql = `DELETE FROM ${table}`;
115
+ const parsedTableName = parseSqlIdentifier(table, "table name");
116
+ this.sql = `DELETE FROM ${parsedTableName}`;
105
117
  return this;
106
118
  }
107
119
  /**
@@ -112,9 +124,16 @@ var SqlBuilder = class {
112
124
  * @returns The builder instance
113
125
  */
114
126
  createTable(table, columnDefinitions, tableConstraints) {
115
- const columns = columnDefinitions.join(", ");
127
+ const parsedTableName = parseSqlIdentifier(table, "table name");
128
+ const parsedColumnDefinitions = columnDefinitions.map((def) => {
129
+ const colName = def.split(/\s+/)[0];
130
+ if (!colName) throw new Error("Empty column name in definition");
131
+ parseSqlIdentifier(colName, "column name");
132
+ return def;
133
+ });
134
+ const columns = parsedColumnDefinitions.join(", ");
116
135
  const constraints = tableConstraints && tableConstraints.length > 0 ? ", " + tableConstraints.join(", ") : "";
117
- this.sql = `CREATE TABLE IF NOT EXISTS ${table} (${columns}${constraints})`;
136
+ this.sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns}${constraints})`;
118
137
  return this;
119
138
  }
120
139
  /**
@@ -137,13 +156,10 @@ var SqlBuilder = class {
137
156
  * @returns The builder instance
138
157
  */
139
158
  createIndex(indexName, tableName, columnName, indexType = "") {
140
- this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${columnName})`;
141
- return this;
142
- }
143
- // Raw SQL with params
144
- raw(sql, ...params) {
145
- this.sql = sql;
146
- this.params.push(...params);
159
+ const parsedIndexName = parseSqlIdentifier(indexName, "index name");
160
+ const parsedTableName = parseSqlIdentifier(tableName, "table name");
161
+ const parsedColumnName = parseSqlIdentifier(columnName, "column name");
162
+ this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${parsedIndexName} ON ${parsedTableName}(${parsedColumnName})`;
147
163
  return this;
148
164
  }
149
165
  /**
@@ -153,11 +169,12 @@ var SqlBuilder = class {
153
169
  * @param exact If true, will not add % wildcards
154
170
  */
155
171
  like(column, value, exact = false) {
172
+ const parsedColumnName = parseSqlIdentifier(column, "column name");
156
173
  const likeValue = exact ? value : `%${value}%`;
157
174
  if (this.whereAdded) {
158
- this.sql += ` AND ${column} LIKE ?`;
175
+ this.sql += ` AND ${parsedColumnName} LIKE ?`;
159
176
  } else {
160
- this.sql += ` WHERE ${column} LIKE ?`;
177
+ this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
161
178
  this.whereAdded = true;
162
179
  }
163
180
  this.params.push(likeValue);
@@ -170,11 +187,13 @@ var SqlBuilder = class {
170
187
  * @param value The value to match
171
188
  */
172
189
  jsonLike(column, key, value) {
173
- const jsonPattern = `%"${key}":"${value}"%`;
190
+ const parsedColumnName = parseSqlIdentifier(column, "column name");
191
+ const parsedKey = parseSqlIdentifier(key, "key name");
192
+ const jsonPattern = `%"${parsedKey}":"${value}"%`;
174
193
  if (this.whereAdded) {
175
- this.sql += ` AND ${column} LIKE ?`;
194
+ this.sql += ` AND ${parsedColumnName} LIKE ?`;
176
195
  } else {
177
- this.sql += ` WHERE ${column} LIKE ?`;
196
+ this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
178
197
  this.whereAdded = true;
179
198
  }
180
199
  this.params.push(jsonPattern);
@@ -204,6 +223,15 @@ var SqlBuilder = class {
204
223
  function createSqlBuilder() {
205
224
  return new SqlBuilder();
206
225
  }
226
+ var SQL_IDENTIFIER_PATTERN = /^[a-zA-Z0-9_]+(\s+AS\s+[a-zA-Z0-9_]+)?$/;
227
+ function parseSelectIdentifier(column) {
228
+ if (column !== "*" && !SQL_IDENTIFIER_PATTERN.test(column)) {
229
+ throw new Error(
230
+ `Invalid column name: "${column}". Must be "*" or a valid identifier (letters, numbers, underscores), optionally with "AS alias".`
231
+ );
232
+ }
233
+ return column;
234
+ }
207
235
 
208
236
  // src/storage/index.ts
209
237
  function isArrayOfRecords(value) {
@@ -222,6 +250,9 @@ var D1Store = class extends MastraStorage {
222
250
  */
223
251
  constructor(config) {
224
252
  super({ name: "D1" });
253
+ if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) {
254
+ throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
255
+ }
225
256
  this.tablePrefix = config.tablePrefix || "";
226
257
  if ("binding" in config) {
227
258
  if (!config.binding) {
@@ -711,7 +742,7 @@ var D1Store = class extends MastraStorage {
711
742
  m.role,
712
743
  m.type,
713
744
  m.createdAt,
714
- m.thread_id AS "threadId"
745
+ m.thread_id AS threadId
715
746
  FROM ordered_messages m
716
747
  WHERE m.id IN (${includeIds.map(() => "?").join(",")})
717
748
  OR EXISTS (
@@ -738,7 +769,7 @@ var D1Store = class extends MastraStorage {
738
769
  if (Array.isArray(includeResult)) messages.push(...includeResult);
739
770
  }
740
771
  const excludeIds = messages.map((m) => m.id);
741
- 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);
772
+ 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);
742
773
  const { sql, params } = query.build();
743
774
  const result = await this.executeQuery({ sql, params });
744
775
  if (Array.isArray(result)) messages.push(...result);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/cloudflare-d1",
3
- "version": "0.1.9",
3
+ "version": "0.2.0-alpha.1",
4
4
  "description": "D1 provider for Mastra - includes db storage capabilities",
5
5
  "type": "module",
6
6
  "files": [
@@ -22,8 +22,7 @@
22
22
  "./package.json": "./package.json"
23
23
  },
24
24
  "dependencies": {
25
- "cloudflare": "^4.1.0",
26
- "@mastra/core": "^0.9.4"
25
+ "cloudflare": "^4.1.0"
27
26
  },
28
27
  "devDependencies": {
29
28
  "@cloudflare/workers-types": "^4.20250417.0",
@@ -35,7 +34,11 @@
35
34
  "tsup": "^8.4.0",
36
35
  "typescript": "^5.8.2",
37
36
  "vitest": "^3.1.2",
38
- "@internal/lint": "0.0.5"
37
+ "@internal/lint": "0.0.5",
38
+ "@mastra/core": "0.10.0-alpha.1"
39
+ },
40
+ "peerDependencies": {
41
+ "@mastra/core": "^0.9.4"
39
42
  },
40
43
  "scripts": {
41
44
  "build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",