@mastra/cloudflare-d1 0.1.9 → 0.1.10-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_tsup-dts-rollup.d.cts +22 -4
- package/dist/_tsup-dts-rollup.d.ts +22 -4
- package/dist/index.cjs +58 -27
- package/dist/index.d.cts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +58 -27
- package/package.json +2 -2
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 ${
|
|
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 ${
|
|
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
|
|
104
|
-
|
|
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
|
-
|
|
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
|
|
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 ${
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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 ${
|
|
181
|
+
this.sql += ` AND ${parsedColumnName} LIKE ?`;
|
|
165
182
|
} else {
|
|
166
|
-
this.sql += ` WHERE ${
|
|
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
|
|
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 ${
|
|
200
|
+
this.sql += ` AND ${parsedColumnName} LIKE ?`;
|
|
182
201
|
} else {
|
|
183
|
-
this.sql += ` WHERE ${
|
|
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
|
|
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",
|
|
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
package/dist/index.d.ts
CHANGED
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 ${
|
|
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 ${
|
|
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
|
|
98
|
-
|
|
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
|
-
|
|
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
|
|
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 ${
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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 ${
|
|
175
|
+
this.sql += ` AND ${parsedColumnName} LIKE ?`;
|
|
159
176
|
} else {
|
|
160
|
-
this.sql += ` WHERE ${
|
|
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
|
|
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 ${
|
|
194
|
+
this.sql += ` AND ${parsedColumnName} LIKE ?`;
|
|
176
195
|
} else {
|
|
177
|
-
this.sql += ` WHERE ${
|
|
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
|
|
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",
|
|
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.
|
|
3
|
+
"version": "0.1.10-alpha.0",
|
|
4
4
|
"description": "D1 provider for Mastra - includes db storage capabilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"cloudflare": "^4.1.0",
|
|
26
|
-
"@mastra/core": "^0.9.
|
|
26
|
+
"@mastra/core": "^0.9.5-alpha.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@cloudflare/workers-types": "^4.20250417.0",
|