@mastra/cloudflare-d1 1.0.0-beta.1 → 1.0.0-beta.11
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/CHANGELOG.md +524 -0
- package/dist/docs/README.md +31 -0
- package/dist/docs/SKILL.md +32 -0
- package/dist/docs/SOURCE_MAP.json +6 -0
- package/dist/docs/storage/01-reference.md +110 -0
- package/dist/index.cjs +1134 -1110
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1134 -1113
- package/dist/index.js.map +1 -1
- package/dist/storage/{domains/operations → db}/index.d.ts +42 -6
- package/dist/storage/db/index.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +8 -7
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/scores/index.d.ts +12 -34
- package/dist/storage/domains/scores/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +14 -14
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +55 -177
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +9 -9
- package/dist/storage/domains/operations/index.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
2
|
-
import {
|
|
2
|
+
import { MemoryStorage, TABLE_SCHEMAS, TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES, ensureDate, createStorageErrorId, normalizePerPage, calculatePagination, serializeDate, ScoresStorage, TABLE_SCORERS, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, MastraStorage, getSqlType, getDefaultValue, transformScoreRow as transformScoreRow$1 } from '@mastra/core/storage';
|
|
3
3
|
import Cloudflare from 'cloudflare';
|
|
4
4
|
import { MessageList } from '@mastra/core/agent';
|
|
5
|
+
import { MastraBase } from '@mastra/core/base';
|
|
5
6
|
import { parseSqlIdentifier } from '@mastra/core/utils';
|
|
6
7
|
import { saveScorePayloadSchema } from '@mastra/core/evals';
|
|
7
8
|
|
|
8
9
|
// src/storage/index.ts
|
|
10
|
+
|
|
11
|
+
// src/storage/domains/utils.ts
|
|
12
|
+
function isArrayOfRecords(value) {
|
|
13
|
+
return value && Array.isArray(value) && value.length > 0;
|
|
14
|
+
}
|
|
15
|
+
function deserializeValue(value, type) {
|
|
16
|
+
if (value === null || value === void 0) return null;
|
|
17
|
+
if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(value);
|
|
20
|
+
} catch {
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
9
26
|
var SqlBuilder = class {
|
|
10
27
|
sql = "";
|
|
11
28
|
params = [];
|
|
@@ -236,1127 +253,1262 @@ function parseSelectIdentifier(column) {
|
|
|
236
253
|
return column;
|
|
237
254
|
}
|
|
238
255
|
|
|
239
|
-
// src/storage/
|
|
240
|
-
function
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
try {
|
|
247
|
-
return JSON.parse(value);
|
|
248
|
-
} catch {
|
|
249
|
-
return value;
|
|
250
|
-
}
|
|
256
|
+
// src/storage/db/index.ts
|
|
257
|
+
function resolveD1Config(config) {
|
|
258
|
+
if ("client" in config) {
|
|
259
|
+
return {
|
|
260
|
+
client: config.client,
|
|
261
|
+
tablePrefix: config.tablePrefix
|
|
262
|
+
};
|
|
251
263
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
operations;
|
|
258
|
-
constructor({ operations }) {
|
|
259
|
-
super();
|
|
260
|
-
this.operations = operations;
|
|
264
|
+
if ("binding" in config) {
|
|
265
|
+
return {
|
|
266
|
+
binding: config.binding,
|
|
267
|
+
tablePrefix: config.tablePrefix
|
|
268
|
+
};
|
|
261
269
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
270
|
+
const cfClient = new Cloudflare({ apiToken: config.apiToken });
|
|
271
|
+
return {
|
|
272
|
+
client: {
|
|
273
|
+
query: ({ sql, params }) => {
|
|
274
|
+
return cfClient.d1.database.query(config.databaseId, {
|
|
275
|
+
account_id: config.accountId,
|
|
276
|
+
sql,
|
|
277
|
+
params
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
tablePrefix: config.tablePrefix
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
var D1DB = class extends MastraBase {
|
|
285
|
+
client;
|
|
286
|
+
binding;
|
|
287
|
+
tablePrefix;
|
|
288
|
+
constructor(config) {
|
|
289
|
+
super({
|
|
290
|
+
component: "STORAGE",
|
|
291
|
+
name: "D1_DB"
|
|
266
292
|
});
|
|
267
|
-
|
|
293
|
+
this.client = config.client;
|
|
294
|
+
this.binding = config.binding;
|
|
295
|
+
this.tablePrefix = config.tablePrefix || "";
|
|
296
|
+
}
|
|
297
|
+
async hasColumn(table, column) {
|
|
298
|
+
const fullTableName = table.startsWith(this.tablePrefix) ? table : `${this.tablePrefix}${table}`;
|
|
299
|
+
const sql = `PRAGMA table_info(${fullTableName});`;
|
|
300
|
+
const result = await this.executeQuery({ sql, params: [] });
|
|
301
|
+
if (!result || !Array.isArray(result)) return false;
|
|
302
|
+
return result.some((col) => col.name === column || col.name === column.toLowerCase());
|
|
303
|
+
}
|
|
304
|
+
getTableName(tableName) {
|
|
305
|
+
return `${this.tablePrefix}${tableName}`;
|
|
306
|
+
}
|
|
307
|
+
formatSqlParams(params) {
|
|
308
|
+
return params.map((p) => p === void 0 || p === null ? null : p);
|
|
309
|
+
}
|
|
310
|
+
async executeWorkersBindingQuery({
|
|
311
|
+
sql,
|
|
312
|
+
params = [],
|
|
313
|
+
first = false
|
|
314
|
+
}) {
|
|
315
|
+
if (!this.binding) {
|
|
316
|
+
throw new Error("Workers binding is not configured");
|
|
317
|
+
}
|
|
268
318
|
try {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
319
|
+
const statement = this.binding.prepare(sql);
|
|
320
|
+
const formattedParams = this.formatSqlParams(params);
|
|
321
|
+
let result;
|
|
322
|
+
if (formattedParams.length > 0) {
|
|
323
|
+
if (first) {
|
|
324
|
+
result = await statement.bind(...formattedParams).first();
|
|
325
|
+
if (!result) return null;
|
|
326
|
+
return result;
|
|
327
|
+
} else {
|
|
328
|
+
result = await statement.bind(...formattedParams).all();
|
|
329
|
+
const results = result.results || [];
|
|
330
|
+
return results;
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
if (first) {
|
|
334
|
+
result = await statement.first();
|
|
335
|
+
if (!result) return null;
|
|
336
|
+
return result;
|
|
337
|
+
} else {
|
|
338
|
+
result = await statement.all();
|
|
339
|
+
const results = result.results || [];
|
|
340
|
+
return results;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
275
343
|
} catch (error) {
|
|
276
|
-
|
|
344
|
+
throw new MastraError(
|
|
277
345
|
{
|
|
278
|
-
id: "
|
|
346
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "WORKERS_BINDING_QUERY", "FAILED"),
|
|
279
347
|
domain: ErrorDomain.STORAGE,
|
|
280
348
|
category: ErrorCategory.THIRD_PARTY,
|
|
281
|
-
|
|
282
|
-
details: { resourceId }
|
|
349
|
+
details: { sql }
|
|
283
350
|
},
|
|
284
351
|
error
|
|
285
352
|
);
|
|
286
|
-
this.logger?.error(mastraError.toString());
|
|
287
|
-
this.logger?.trackException(mastraError);
|
|
288
|
-
return null;
|
|
289
353
|
}
|
|
290
354
|
}
|
|
291
|
-
async
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
};
|
|
300
|
-
const processedRecord = await this.operations.processRecord(resourceToSave);
|
|
301
|
-
const columns = Object.keys(processedRecord);
|
|
302
|
-
const values = Object.values(processedRecord);
|
|
303
|
-
const updateMap = {
|
|
304
|
-
workingMemory: "excluded.workingMemory",
|
|
305
|
-
metadata: "excluded.metadata",
|
|
306
|
-
createdAt: "excluded.createdAt",
|
|
307
|
-
updatedAt: "excluded.updatedAt"
|
|
308
|
-
};
|
|
309
|
-
const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], updateMap);
|
|
310
|
-
const { sql, params } = query.build();
|
|
355
|
+
async executeRestQuery({
|
|
356
|
+
sql,
|
|
357
|
+
params = [],
|
|
358
|
+
first = false
|
|
359
|
+
}) {
|
|
360
|
+
if (!this.client) {
|
|
361
|
+
throw new Error("D1 client is not configured");
|
|
362
|
+
}
|
|
311
363
|
try {
|
|
312
|
-
|
|
313
|
-
|
|
364
|
+
const formattedParams = this.formatSqlParams(params);
|
|
365
|
+
const response = await this.client.query({
|
|
366
|
+
sql,
|
|
367
|
+
params: formattedParams
|
|
368
|
+
});
|
|
369
|
+
const result = response.result || [];
|
|
370
|
+
const results = result.flatMap((r) => r.results || []);
|
|
371
|
+
if (first) {
|
|
372
|
+
return results[0] || null;
|
|
373
|
+
}
|
|
374
|
+
return results;
|
|
314
375
|
} catch (error) {
|
|
315
376
|
throw new MastraError(
|
|
316
377
|
{
|
|
317
|
-
id: "
|
|
378
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "REST_QUERY", "FAILED"),
|
|
318
379
|
domain: ErrorDomain.STORAGE,
|
|
319
380
|
category: ErrorCategory.THIRD_PARTY,
|
|
320
|
-
|
|
321
|
-
details: { resourceId: resource.id }
|
|
381
|
+
details: { sql }
|
|
322
382
|
},
|
|
323
383
|
error
|
|
324
384
|
);
|
|
325
385
|
}
|
|
326
386
|
}
|
|
327
|
-
async
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const newResource = {
|
|
335
|
-
id: resourceId,
|
|
336
|
-
workingMemory,
|
|
337
|
-
metadata: metadata || {},
|
|
338
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
339
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
340
|
-
};
|
|
341
|
-
return this.saveResource({ resource: newResource });
|
|
387
|
+
async executeQuery(options) {
|
|
388
|
+
if (this.binding) {
|
|
389
|
+
return this.executeWorkersBindingQuery(options);
|
|
390
|
+
} else if (this.client) {
|
|
391
|
+
return this.executeRestQuery(options);
|
|
392
|
+
} else {
|
|
393
|
+
throw new Error("Neither binding nor client is configured");
|
|
342
394
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
...existingResource,
|
|
346
|
-
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
347
|
-
metadata: {
|
|
348
|
-
...existingResource.metadata,
|
|
349
|
-
...metadata
|
|
350
|
-
},
|
|
351
|
-
updatedAt
|
|
352
|
-
};
|
|
353
|
-
const fullTableName = this.operations.getTableName(TABLE_RESOURCES);
|
|
354
|
-
const columns = ["workingMemory", "metadata", "updatedAt"];
|
|
355
|
-
const values = [updatedResource.workingMemory, JSON.stringify(updatedResource.metadata), updatedAt.toISOString()];
|
|
356
|
-
const query = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", resourceId);
|
|
357
|
-
const { sql, params } = query.build();
|
|
395
|
+
}
|
|
396
|
+
async getTableColumns(tableName) {
|
|
358
397
|
try {
|
|
359
|
-
|
|
360
|
-
|
|
398
|
+
const sql = `PRAGMA table_info(${tableName})`;
|
|
399
|
+
const result = await this.executeQuery({ sql });
|
|
400
|
+
if (!result || !Array.isArray(result)) {
|
|
401
|
+
return [];
|
|
402
|
+
}
|
|
403
|
+
return result.map((row) => ({
|
|
404
|
+
name: row.name,
|
|
405
|
+
type: row.type
|
|
406
|
+
}));
|
|
407
|
+
} catch (error) {
|
|
408
|
+
this.logger.warn(`Failed to get table columns for ${tableName}:`, error);
|
|
409
|
+
return [];
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
serializeValue(value) {
|
|
413
|
+
if (value === null || value === void 0) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
if (value instanceof Date) {
|
|
417
|
+
return value.toISOString();
|
|
418
|
+
}
|
|
419
|
+
if (typeof value === "object") {
|
|
420
|
+
return JSON.stringify(value);
|
|
421
|
+
}
|
|
422
|
+
return value;
|
|
423
|
+
}
|
|
424
|
+
getSqlType(type) {
|
|
425
|
+
switch (type) {
|
|
426
|
+
case "bigint":
|
|
427
|
+
return "INTEGER";
|
|
428
|
+
// SQLite uses INTEGER for all integer sizes
|
|
429
|
+
case "jsonb":
|
|
430
|
+
return "TEXT";
|
|
431
|
+
// Store JSON as TEXT in SQLite
|
|
432
|
+
case "boolean":
|
|
433
|
+
return "INTEGER";
|
|
434
|
+
// SQLite uses 0/1 for booleans
|
|
435
|
+
default:
|
|
436
|
+
return getSqlType(type);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
getDefaultValue(type) {
|
|
440
|
+
return getDefaultValue(type);
|
|
441
|
+
}
|
|
442
|
+
async createTable({
|
|
443
|
+
tableName,
|
|
444
|
+
schema
|
|
445
|
+
}) {
|
|
446
|
+
try {
|
|
447
|
+
const fullTableName = this.getTableName(tableName);
|
|
448
|
+
const columnDefinitions = Object.entries(schema).map(([colName, colDef]) => {
|
|
449
|
+
const type = this.getSqlType(colDef.type);
|
|
450
|
+
const nullable = colDef.nullable === false ? "NOT NULL" : "";
|
|
451
|
+
const primaryKey = colDef.primaryKey ? "PRIMARY KEY" : "";
|
|
452
|
+
return `${colName} ${type} ${nullable} ${primaryKey}`.trim();
|
|
453
|
+
});
|
|
454
|
+
const tableConstraints = [];
|
|
455
|
+
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
456
|
+
tableConstraints.push("UNIQUE (workflow_name, run_id)");
|
|
457
|
+
}
|
|
458
|
+
const query = createSqlBuilder().createTable(fullTableName, columnDefinitions, tableConstraints);
|
|
459
|
+
const { sql, params } = query.build();
|
|
460
|
+
await this.executeQuery({ sql, params });
|
|
461
|
+
this.logger.debug(`Created table ${fullTableName}`);
|
|
361
462
|
} catch (error) {
|
|
362
463
|
throw new MastraError(
|
|
363
464
|
{
|
|
364
|
-
id: "
|
|
465
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "CREATE_TABLE", "FAILED"),
|
|
365
466
|
domain: ErrorDomain.STORAGE,
|
|
366
467
|
category: ErrorCategory.THIRD_PARTY,
|
|
367
|
-
|
|
368
|
-
details: { resourceId }
|
|
468
|
+
details: { tableName }
|
|
369
469
|
},
|
|
370
470
|
error
|
|
371
471
|
);
|
|
372
472
|
}
|
|
373
473
|
}
|
|
374
|
-
async
|
|
375
|
-
const thread = await this.operations.load({
|
|
376
|
-
tableName: TABLE_THREADS,
|
|
377
|
-
keys: { id: threadId }
|
|
378
|
-
});
|
|
379
|
-
if (!thread) return null;
|
|
474
|
+
async clearTable({ tableName }) {
|
|
380
475
|
try {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
};
|
|
476
|
+
const fullTableName = this.getTableName(tableName);
|
|
477
|
+
const query = createSqlBuilder().delete(fullTableName);
|
|
478
|
+
const { sql, params } = query.build();
|
|
479
|
+
await this.executeQuery({ sql, params });
|
|
480
|
+
this.logger.debug(`Cleared table ${fullTableName}`);
|
|
387
481
|
} catch (error) {
|
|
388
|
-
|
|
482
|
+
throw new MastraError(
|
|
389
483
|
{
|
|
390
|
-
id: "
|
|
484
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "CLEAR_TABLE", "FAILED"),
|
|
391
485
|
domain: ErrorDomain.STORAGE,
|
|
392
486
|
category: ErrorCategory.THIRD_PARTY,
|
|
393
|
-
|
|
394
|
-
details: { threadId }
|
|
487
|
+
details: { tableName }
|
|
395
488
|
},
|
|
396
489
|
error
|
|
397
490
|
);
|
|
398
|
-
this.logger?.error(mastraError.toString());
|
|
399
|
-
this.logger?.trackException(mastraError);
|
|
400
|
-
return null;
|
|
401
491
|
}
|
|
402
492
|
}
|
|
403
|
-
async
|
|
404
|
-
const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
|
|
405
|
-
const perPage = normalizePerPage(perPageInput, 100);
|
|
406
|
-
if (page < 0) {
|
|
407
|
-
throw new MastraError(
|
|
408
|
-
{
|
|
409
|
-
id: "STORAGE_CLOUDFLARE_D1_LIST_THREADS_BY_RESOURCE_ID_INVALID_PAGE",
|
|
410
|
-
domain: ErrorDomain.STORAGE,
|
|
411
|
-
category: ErrorCategory.USER,
|
|
412
|
-
details: { page }
|
|
413
|
-
},
|
|
414
|
-
new Error("page must be >= 0")
|
|
415
|
-
);
|
|
416
|
-
}
|
|
417
|
-
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
418
|
-
const { field, direction } = this.parseOrderBy(orderBy);
|
|
419
|
-
const fullTableName = this.operations.getTableName(TABLE_THREADS);
|
|
420
|
-
const mapRowToStorageThreadType = (row) => ({
|
|
421
|
-
...row,
|
|
422
|
-
createdAt: ensureDate(row.createdAt),
|
|
423
|
-
updatedAt: ensureDate(row.updatedAt),
|
|
424
|
-
metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata || "{}") : row.metadata || {}
|
|
425
|
-
});
|
|
493
|
+
async dropTable({ tableName }) {
|
|
426
494
|
try {
|
|
427
|
-
const
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("resourceId = ?", resourceId).orderBy(field, direction).limit(limitValue).offset(offset);
|
|
432
|
-
const results = await this.operations.executeQuery(selectQuery.build());
|
|
433
|
-
const threads = results.map(mapRowToStorageThreadType);
|
|
434
|
-
return {
|
|
435
|
-
threads,
|
|
436
|
-
total,
|
|
437
|
-
page,
|
|
438
|
-
perPage: perPageForResponse,
|
|
439
|
-
hasMore: perPageInput === false ? false : offset + perPage < total
|
|
440
|
-
};
|
|
495
|
+
const fullTableName = this.getTableName(tableName);
|
|
496
|
+
const sql = `DROP TABLE IF EXISTS ${fullTableName}`;
|
|
497
|
+
await this.executeQuery({ sql });
|
|
498
|
+
this.logger.debug(`Dropped table ${fullTableName}`);
|
|
441
499
|
} catch (error) {
|
|
442
|
-
|
|
500
|
+
throw new MastraError(
|
|
443
501
|
{
|
|
444
|
-
id: "
|
|
502
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "DROP_TABLE", "FAILED"),
|
|
445
503
|
domain: ErrorDomain.STORAGE,
|
|
446
504
|
category: ErrorCategory.THIRD_PARTY,
|
|
447
|
-
|
|
448
|
-
details: { resourceId }
|
|
505
|
+
details: { tableName }
|
|
449
506
|
},
|
|
450
507
|
error
|
|
451
508
|
);
|
|
452
|
-
this.logger?.error(mastraError.toString());
|
|
453
|
-
this.logger?.trackException(mastraError);
|
|
454
|
-
return {
|
|
455
|
-
threads: [],
|
|
456
|
-
total: 0,
|
|
457
|
-
page,
|
|
458
|
-
perPage: perPageForResponse,
|
|
459
|
-
hasMore: false
|
|
460
|
-
};
|
|
461
509
|
}
|
|
462
510
|
}
|
|
463
|
-
async
|
|
464
|
-
const fullTableName = this.operations.getTableName(TABLE_THREADS);
|
|
465
|
-
const threadToSave = {
|
|
466
|
-
id: thread.id,
|
|
467
|
-
resourceId: thread.resourceId,
|
|
468
|
-
title: thread.title,
|
|
469
|
-
metadata: thread.metadata ? JSON.stringify(thread.metadata) : null,
|
|
470
|
-
createdAt: thread.createdAt.toISOString(),
|
|
471
|
-
updatedAt: thread.updatedAt.toISOString()
|
|
472
|
-
};
|
|
473
|
-
const processedRecord = await this.operations.processRecord(threadToSave);
|
|
474
|
-
const columns = Object.keys(processedRecord);
|
|
475
|
-
const values = Object.values(processedRecord);
|
|
476
|
-
const updateMap = {
|
|
477
|
-
resourceId: "excluded.resourceId",
|
|
478
|
-
title: "excluded.title",
|
|
479
|
-
metadata: "excluded.metadata",
|
|
480
|
-
createdAt: "excluded.createdAt",
|
|
481
|
-
updatedAt: "excluded.updatedAt"
|
|
482
|
-
};
|
|
483
|
-
const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], updateMap);
|
|
484
|
-
const { sql, params } = query.build();
|
|
511
|
+
async alterTable(args) {
|
|
485
512
|
try {
|
|
486
|
-
|
|
487
|
-
|
|
513
|
+
const fullTableName = this.getTableName(args.tableName);
|
|
514
|
+
const existingColumns = await this.getTableColumns(fullTableName);
|
|
515
|
+
const existingColumnNames = new Set(existingColumns.map((col) => col.name));
|
|
516
|
+
for (const [columnName, column] of Object.entries(args.schema)) {
|
|
517
|
+
if (!existingColumnNames.has(columnName) && args.ifNotExists.includes(columnName)) {
|
|
518
|
+
const sqlType = this.getSqlType(column.type);
|
|
519
|
+
const defaultValue = this.getDefaultValue(column.type);
|
|
520
|
+
const sql = `ALTER TABLE ${fullTableName} ADD COLUMN ${columnName} ${sqlType} ${defaultValue}`;
|
|
521
|
+
await this.executeQuery({ sql });
|
|
522
|
+
this.logger.debug(`Added column ${columnName} to table ${fullTableName}`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
488
525
|
} catch (error) {
|
|
489
526
|
throw new MastraError(
|
|
490
527
|
{
|
|
491
|
-
id: "
|
|
528
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "ALTER_TABLE", "FAILED"),
|
|
492
529
|
domain: ErrorDomain.STORAGE,
|
|
493
530
|
category: ErrorCategory.THIRD_PARTY,
|
|
494
|
-
|
|
495
|
-
details: { threadId: thread.id }
|
|
531
|
+
details: { tableName: args.tableName }
|
|
496
532
|
},
|
|
497
533
|
error
|
|
498
534
|
);
|
|
499
535
|
}
|
|
500
536
|
}
|
|
501
|
-
async
|
|
502
|
-
id,
|
|
503
|
-
title,
|
|
504
|
-
metadata
|
|
505
|
-
}) {
|
|
506
|
-
const thread = await this.getThreadById({ threadId: id });
|
|
537
|
+
async insert({ tableName, record }) {
|
|
507
538
|
try {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
const
|
|
512
|
-
const
|
|
513
|
-
...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
514
|
-
...metadata
|
|
515
|
-
};
|
|
516
|
-
const updatedAt = /* @__PURE__ */ new Date();
|
|
517
|
-
const columns = ["title", "metadata", "updatedAt"];
|
|
518
|
-
const values = [title, JSON.stringify(mergedMetadata), updatedAt.toISOString()];
|
|
519
|
-
const query = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", id);
|
|
539
|
+
const fullTableName = this.getTableName(tableName);
|
|
540
|
+
const processedRecord = await this.processRecord(record);
|
|
541
|
+
const columns = Object.keys(processedRecord);
|
|
542
|
+
const values = Object.values(processedRecord);
|
|
543
|
+
const query = createSqlBuilder().insert(fullTableName, columns, values);
|
|
520
544
|
const { sql, params } = query.build();
|
|
521
|
-
await this.
|
|
522
|
-
return {
|
|
523
|
-
...thread,
|
|
524
|
-
title,
|
|
525
|
-
metadata: {
|
|
526
|
-
...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
527
|
-
...metadata
|
|
528
|
-
},
|
|
529
|
-
updatedAt
|
|
530
|
-
};
|
|
545
|
+
await this.executeQuery({ sql, params });
|
|
531
546
|
} catch (error) {
|
|
532
547
|
throw new MastraError(
|
|
533
548
|
{
|
|
534
|
-
id: "
|
|
549
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "INSERT", "FAILED"),
|
|
535
550
|
domain: ErrorDomain.STORAGE,
|
|
536
551
|
category: ErrorCategory.THIRD_PARTY,
|
|
537
|
-
|
|
538
|
-
details: { threadId: id }
|
|
552
|
+
details: { tableName }
|
|
539
553
|
},
|
|
540
554
|
error
|
|
541
555
|
);
|
|
542
556
|
}
|
|
543
557
|
}
|
|
544
|
-
async
|
|
545
|
-
const fullTableName = this.operations.getTableName(TABLE_THREADS);
|
|
558
|
+
async batchInsert({ tableName, records }) {
|
|
546
559
|
try {
|
|
547
|
-
|
|
548
|
-
const
|
|
549
|
-
await
|
|
550
|
-
const
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
560
|
+
if (records.length === 0) return;
|
|
561
|
+
const fullTableName = this.getTableName(tableName);
|
|
562
|
+
const processedRecords = await Promise.all(records.map((record) => this.processRecord(record)));
|
|
563
|
+
const columns = Object.keys(processedRecords[0] || {});
|
|
564
|
+
for (const record of processedRecords) {
|
|
565
|
+
const values = Object.values(record);
|
|
566
|
+
const query = createSqlBuilder().insert(fullTableName, columns, values);
|
|
567
|
+
const { sql, params } = query.build();
|
|
568
|
+
await this.executeQuery({ sql, params });
|
|
569
|
+
}
|
|
554
570
|
} catch (error) {
|
|
555
571
|
throw new MastraError(
|
|
556
572
|
{
|
|
557
|
-
id: "
|
|
573
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "BATCH_INSERT", "FAILED"),
|
|
558
574
|
domain: ErrorDomain.STORAGE,
|
|
559
575
|
category: ErrorCategory.THIRD_PARTY,
|
|
560
|
-
|
|
561
|
-
details: { threadId }
|
|
576
|
+
details: { tableName }
|
|
562
577
|
},
|
|
563
578
|
error
|
|
564
579
|
);
|
|
565
580
|
}
|
|
566
581
|
}
|
|
567
|
-
async
|
|
568
|
-
const { messages } = args;
|
|
569
|
-
if (messages.length === 0) return { messages: [] };
|
|
582
|
+
async load({ tableName, keys }) {
|
|
570
583
|
try {
|
|
571
|
-
const
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
if (
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
}
|
|
581
|
-
if (!message.role) {
|
|
582
|
-
throw new Error(`Message at index ${i} missing role`);
|
|
583
|
-
}
|
|
584
|
-
if (!message.resourceId) {
|
|
585
|
-
throw new Error(`Message at index ${i} missing resourceId`);
|
|
586
|
-
}
|
|
587
|
-
const thread = await this.getThreadById({ threadId: message.threadId });
|
|
588
|
-
if (!thread) {
|
|
589
|
-
throw new Error(`Thread ${message.threadId} not found`);
|
|
584
|
+
const fullTableName = this.getTableName(tableName);
|
|
585
|
+
const query = createSqlBuilder().select("*").from(fullTableName);
|
|
586
|
+
let firstKey = true;
|
|
587
|
+
for (const [key, value] of Object.entries(keys)) {
|
|
588
|
+
if (firstKey) {
|
|
589
|
+
query.where(`${key} = ?`, value);
|
|
590
|
+
firstKey = false;
|
|
591
|
+
} else {
|
|
592
|
+
query.andWhere(`${key} = ?`, value);
|
|
590
593
|
}
|
|
591
594
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
await Promise.all([
|
|
605
|
-
this.operations.batchUpsert({
|
|
606
|
-
tableName: TABLE_MESSAGES,
|
|
607
|
-
records: messagesToInsert
|
|
608
|
-
}),
|
|
609
|
-
// Update thread's updatedAt timestamp
|
|
610
|
-
this.operations.executeQuery({
|
|
611
|
-
sql: `UPDATE ${this.operations.getTableName(TABLE_THREADS)} SET updatedAt = ? WHERE id = ?`,
|
|
612
|
-
params: [now.toISOString(), threadId]
|
|
613
|
-
})
|
|
614
|
-
]);
|
|
615
|
-
this.logger.debug(`Saved ${messages.length} messages`);
|
|
616
|
-
const list = new MessageList().add(messages, "memory");
|
|
617
|
-
return { messages: list.get.all.db() };
|
|
595
|
+
query.orderBy("createdAt", "DESC");
|
|
596
|
+
query.limit(1);
|
|
597
|
+
const { sql, params } = query.build();
|
|
598
|
+
const result = await this.executeQuery({ sql, params, first: true });
|
|
599
|
+
if (!result) {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
const deserializedResult = {};
|
|
603
|
+
for (const [key, value] of Object.entries(result)) {
|
|
604
|
+
deserializedResult[key] = deserializeValue(value);
|
|
605
|
+
}
|
|
606
|
+
return deserializedResult;
|
|
618
607
|
} catch (error) {
|
|
619
608
|
throw new MastraError(
|
|
620
609
|
{
|
|
621
|
-
id: "
|
|
610
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LOAD", "FAILED"),
|
|
622
611
|
domain: ErrorDomain.STORAGE,
|
|
623
612
|
category: ErrorCategory.THIRD_PARTY,
|
|
624
|
-
|
|
613
|
+
details: { tableName }
|
|
625
614
|
},
|
|
626
615
|
error
|
|
627
616
|
);
|
|
628
617
|
}
|
|
629
618
|
}
|
|
630
|
-
async
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
const params = [];
|
|
635
|
-
let paramIdx = 1;
|
|
636
|
-
for (const inc of include) {
|
|
637
|
-
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
638
|
-
const searchId = inc.threadId || threadId;
|
|
639
|
-
unionQueries.push(`
|
|
640
|
-
SELECT * FROM (
|
|
641
|
-
WITH ordered_messages AS (
|
|
642
|
-
SELECT
|
|
643
|
-
*,
|
|
644
|
-
ROW_NUMBER() OVER (ORDER BY createdAt ASC) AS row_num
|
|
645
|
-
FROM ${this.operations.getTableName(TABLE_MESSAGES)}
|
|
646
|
-
WHERE thread_id = ?
|
|
647
|
-
)
|
|
648
|
-
SELECT
|
|
649
|
-
m.id,
|
|
650
|
-
m.content,
|
|
651
|
-
m.role,
|
|
652
|
-
m.type,
|
|
653
|
-
m.createdAt,
|
|
654
|
-
m.thread_id AS threadId,
|
|
655
|
-
m.resourceId
|
|
656
|
-
FROM ordered_messages m
|
|
657
|
-
WHERE m.id = ?
|
|
658
|
-
OR EXISTS (
|
|
659
|
-
SELECT 1 FROM ordered_messages target
|
|
660
|
-
WHERE target.id = ?
|
|
661
|
-
AND (
|
|
662
|
-
(m.row_num <= target.row_num + ? AND m.row_num > target.row_num)
|
|
663
|
-
OR
|
|
664
|
-
(m.row_num >= target.row_num - ? AND m.row_num < target.row_num)
|
|
665
|
-
)
|
|
666
|
-
)
|
|
667
|
-
) AS query_${paramIdx}
|
|
668
|
-
`);
|
|
669
|
-
params.push(searchId, id, id, withNextMessages, withPreviousMessages);
|
|
670
|
-
paramIdx++;
|
|
671
|
-
}
|
|
672
|
-
const finalQuery = unionQueries.join(" UNION ALL ") + " ORDER BY createdAt ASC";
|
|
673
|
-
const messages = await this.operations.executeQuery({ sql: finalQuery, params });
|
|
674
|
-
if (!Array.isArray(messages)) {
|
|
675
|
-
return [];
|
|
619
|
+
async processRecord(record) {
|
|
620
|
+
const processed = {};
|
|
621
|
+
for (const [key, value] of Object.entries(record)) {
|
|
622
|
+
processed[key] = this.serializeValue(value);
|
|
676
623
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
624
|
+
return processed;
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Upsert multiple records in a batch operation
|
|
628
|
+
* @param tableName The table to insert into
|
|
629
|
+
* @param records The records to insert
|
|
630
|
+
*/
|
|
631
|
+
async batchUpsert({ tableName, records }) {
|
|
632
|
+
if (records.length === 0) return;
|
|
633
|
+
const fullTableName = this.getTableName(tableName);
|
|
634
|
+
try {
|
|
635
|
+
const batchSize = 50;
|
|
636
|
+
for (let i = 0; i < records.length; i += batchSize) {
|
|
637
|
+
const batch = records.slice(i, i + batchSize);
|
|
638
|
+
const recordsToInsert = batch;
|
|
639
|
+
if (recordsToInsert.length > 0) {
|
|
640
|
+
const firstRecord = recordsToInsert[0];
|
|
641
|
+
const columns = Object.keys(firstRecord || {});
|
|
642
|
+
for (const record of recordsToInsert) {
|
|
643
|
+
const values = columns.map((col) => {
|
|
644
|
+
if (!record) return null;
|
|
645
|
+
const value = typeof col === "string" ? record[col] : null;
|
|
646
|
+
return this.serializeValue(value);
|
|
647
|
+
});
|
|
648
|
+
const recordToUpsert = columns.reduce(
|
|
649
|
+
(acc, col) => {
|
|
650
|
+
if (col !== "createdAt") acc[col] = `excluded.${col}`;
|
|
651
|
+
return acc;
|
|
652
|
+
},
|
|
653
|
+
{}
|
|
654
|
+
);
|
|
655
|
+
const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], recordToUpsert);
|
|
656
|
+
const { sql, params } = query.build();
|
|
657
|
+
await this.executeQuery({ sql, params });
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
this.logger.debug(
|
|
661
|
+
`Processed batch ${Math.floor(i / batchSize) + 1} of ${Math.ceil(records.length / batchSize)}`
|
|
662
|
+
);
|
|
682
663
|
}
|
|
683
|
-
|
|
664
|
+
this.logger.debug(`Successfully batch upserted ${records.length} records into ${tableName}`);
|
|
665
|
+
} catch (error) {
|
|
666
|
+
throw new MastraError(
|
|
667
|
+
{
|
|
668
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "BATCH_UPSERT", "FAILED"),
|
|
669
|
+
domain: ErrorDomain.STORAGE,
|
|
670
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
671
|
+
text: `Failed to batch upsert into ${tableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
672
|
+
details: { tableName }
|
|
673
|
+
},
|
|
674
|
+
error
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
// src/storage/domains/memory/index.ts
|
|
681
|
+
var MemoryStorageD1 = class extends MemoryStorage {
|
|
682
|
+
#db;
|
|
683
|
+
constructor(config) {
|
|
684
|
+
super();
|
|
685
|
+
this.#db = new D1DB(resolveD1Config(config));
|
|
686
|
+
}
|
|
687
|
+
async init() {
|
|
688
|
+
await this.#db.createTable({ tableName: TABLE_THREADS, schema: TABLE_SCHEMAS[TABLE_THREADS] });
|
|
689
|
+
await this.#db.createTable({ tableName: TABLE_MESSAGES, schema: TABLE_SCHEMAS[TABLE_MESSAGES] });
|
|
690
|
+
await this.#db.createTable({ tableName: TABLE_RESOURCES, schema: TABLE_SCHEMAS[TABLE_RESOURCES] });
|
|
691
|
+
await this.#db.alterTable({
|
|
692
|
+
tableName: TABLE_MESSAGES,
|
|
693
|
+
schema: TABLE_SCHEMAS[TABLE_MESSAGES],
|
|
694
|
+
ifNotExists: ["resourceId"]
|
|
684
695
|
});
|
|
685
|
-
return processedMessages;
|
|
686
696
|
}
|
|
687
|
-
async
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
697
|
+
async dangerouslyClearAll() {
|
|
698
|
+
await this.#db.clearTable({ tableName: TABLE_MESSAGES });
|
|
699
|
+
await this.#db.clearTable({ tableName: TABLE_THREADS });
|
|
700
|
+
await this.#db.clearTable({ tableName: TABLE_RESOURCES });
|
|
701
|
+
}
|
|
702
|
+
async getResourceById({ resourceId }) {
|
|
703
|
+
const resource = await this.#db.load({
|
|
704
|
+
tableName: TABLE_RESOURCES,
|
|
705
|
+
keys: { id: resourceId }
|
|
706
|
+
});
|
|
707
|
+
if (!resource) return null;
|
|
691
708
|
try {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
const processedMsg = {};
|
|
699
|
-
for (const [key, value] of Object.entries(message)) {
|
|
700
|
-
if (key === `type` && value === `v2`) continue;
|
|
701
|
-
processedMsg[key] = deserializeValue(value);
|
|
702
|
-
}
|
|
703
|
-
return processedMsg;
|
|
704
|
-
});
|
|
705
|
-
this.logger.debug(`Retrieved ${messages.length} messages`);
|
|
706
|
-
const list = new MessageList().add(processedMessages, "memory");
|
|
707
|
-
return { messages: list.get.all.db() };
|
|
709
|
+
return {
|
|
710
|
+
...resource,
|
|
711
|
+
createdAt: ensureDate(resource.createdAt),
|
|
712
|
+
updatedAt: ensureDate(resource.updatedAt),
|
|
713
|
+
metadata: typeof resource.metadata === "string" ? JSON.parse(resource.metadata || "{}") : resource.metadata
|
|
714
|
+
};
|
|
708
715
|
} catch (error) {
|
|
709
716
|
const mastraError = new MastraError(
|
|
710
717
|
{
|
|
711
|
-
id: "
|
|
718
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_RESOURCE_BY_ID", "FAILED"),
|
|
712
719
|
domain: ErrorDomain.STORAGE,
|
|
713
720
|
category: ErrorCategory.THIRD_PARTY,
|
|
714
|
-
text: `
|
|
715
|
-
details: {
|
|
721
|
+
text: `Error processing resource ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
722
|
+
details: { resourceId }
|
|
716
723
|
},
|
|
717
724
|
error
|
|
718
725
|
);
|
|
719
726
|
this.logger?.error(mastraError.toString());
|
|
720
727
|
this.logger?.trackException(mastraError);
|
|
721
|
-
|
|
728
|
+
return null;
|
|
722
729
|
}
|
|
723
730
|
}
|
|
724
|
-
async
|
|
725
|
-
const
|
|
726
|
-
|
|
731
|
+
async saveResource({ resource }) {
|
|
732
|
+
const fullTableName = this.#db.getTableName(TABLE_RESOURCES);
|
|
733
|
+
const resourceToSave = {
|
|
734
|
+
id: resource.id,
|
|
735
|
+
workingMemory: resource.workingMemory,
|
|
736
|
+
metadata: resource.metadata ? JSON.stringify(resource.metadata) : null,
|
|
737
|
+
createdAt: resource.createdAt,
|
|
738
|
+
updatedAt: resource.updatedAt
|
|
739
|
+
};
|
|
740
|
+
const processedRecord = await this.#db.processRecord(resourceToSave);
|
|
741
|
+
const columns = Object.keys(processedRecord);
|
|
742
|
+
const values = Object.values(processedRecord);
|
|
743
|
+
const updateMap = {
|
|
744
|
+
workingMemory: "excluded.workingMemory",
|
|
745
|
+
metadata: "excluded.metadata",
|
|
746
|
+
createdAt: "excluded.createdAt",
|
|
747
|
+
updatedAt: "excluded.updatedAt"
|
|
748
|
+
};
|
|
749
|
+
const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], updateMap);
|
|
750
|
+
const { sql, params } = query.build();
|
|
751
|
+
try {
|
|
752
|
+
await this.#db.executeQuery({ sql, params });
|
|
753
|
+
return resource;
|
|
754
|
+
} catch (error) {
|
|
727
755
|
throw new MastraError(
|
|
728
756
|
{
|
|
729
|
-
id: "
|
|
757
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_RESOURCE", "FAILED"),
|
|
730
758
|
domain: ErrorDomain.STORAGE,
|
|
731
759
|
category: ErrorCategory.THIRD_PARTY,
|
|
732
|
-
|
|
760
|
+
text: `Failed to save resource to ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
761
|
+
details: { resourceId: resource.id }
|
|
733
762
|
},
|
|
734
|
-
|
|
763
|
+
error
|
|
735
764
|
);
|
|
736
765
|
}
|
|
737
|
-
|
|
766
|
+
}
|
|
767
|
+
async updateResource({
|
|
768
|
+
resourceId,
|
|
769
|
+
workingMemory,
|
|
770
|
+
metadata
|
|
771
|
+
}) {
|
|
772
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
773
|
+
if (!existingResource) {
|
|
774
|
+
const newResource = {
|
|
775
|
+
id: resourceId,
|
|
776
|
+
workingMemory,
|
|
777
|
+
metadata: metadata || {},
|
|
778
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
779
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
780
|
+
};
|
|
781
|
+
return this.saveResource({ resource: newResource });
|
|
782
|
+
}
|
|
783
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
784
|
+
const updatedResource = {
|
|
785
|
+
...existingResource,
|
|
786
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
787
|
+
metadata: {
|
|
788
|
+
...existingResource.metadata,
|
|
789
|
+
...metadata
|
|
790
|
+
},
|
|
791
|
+
updatedAt
|
|
792
|
+
};
|
|
793
|
+
const fullTableName = this.#db.getTableName(TABLE_RESOURCES);
|
|
794
|
+
const columns = ["workingMemory", "metadata", "updatedAt"];
|
|
795
|
+
const values = [updatedResource.workingMemory, JSON.stringify(updatedResource.metadata), updatedAt.toISOString()];
|
|
796
|
+
const query = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", resourceId);
|
|
797
|
+
const { sql, params } = query.build();
|
|
798
|
+
try {
|
|
799
|
+
await this.#db.executeQuery({ sql, params });
|
|
800
|
+
return updatedResource;
|
|
801
|
+
} catch (error) {
|
|
738
802
|
throw new MastraError(
|
|
739
803
|
{
|
|
740
|
-
id: "
|
|
804
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "UPDATE_RESOURCE", "FAILED"),
|
|
741
805
|
domain: ErrorDomain.STORAGE,
|
|
742
|
-
category: ErrorCategory.
|
|
743
|
-
|
|
806
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
807
|
+
text: `Failed to update resource ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
808
|
+
details: { resourceId }
|
|
744
809
|
},
|
|
745
|
-
|
|
810
|
+
error
|
|
746
811
|
);
|
|
747
812
|
}
|
|
748
|
-
|
|
749
|
-
|
|
813
|
+
}
|
|
814
|
+
async getThreadById({ threadId }) {
|
|
815
|
+
const thread = await this.#db.load({
|
|
816
|
+
tableName: TABLE_THREADS,
|
|
817
|
+
keys: { id: threadId }
|
|
818
|
+
});
|
|
819
|
+
if (!thread) return null;
|
|
750
820
|
try {
|
|
751
|
-
const fullTableName = this.operations.getTableName(TABLE_MESSAGES);
|
|
752
|
-
let query = `
|
|
753
|
-
SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId
|
|
754
|
-
FROM ${fullTableName}
|
|
755
|
-
WHERE thread_id = ?
|
|
756
|
-
`;
|
|
757
|
-
const queryParams = [threadId];
|
|
758
|
-
if (resourceId) {
|
|
759
|
-
query += ` AND resourceId = ?`;
|
|
760
|
-
queryParams.push(resourceId);
|
|
761
|
-
}
|
|
762
|
-
const dateRange = filter?.dateRange;
|
|
763
|
-
if (dateRange?.start) {
|
|
764
|
-
const startDate = dateRange.start instanceof Date ? serializeDate(dateRange.start) : serializeDate(new Date(dateRange.start));
|
|
765
|
-
query += ` AND createdAt >= ?`;
|
|
766
|
-
queryParams.push(startDate);
|
|
767
|
-
}
|
|
768
|
-
if (dateRange?.end) {
|
|
769
|
-
const endDate = dateRange.end instanceof Date ? serializeDate(dateRange.end) : serializeDate(new Date(dateRange.end));
|
|
770
|
-
query += ` AND createdAt <= ?`;
|
|
771
|
-
queryParams.push(endDate);
|
|
772
|
-
}
|
|
773
|
-
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
774
|
-
query += ` ORDER BY "${field}" ${direction}`;
|
|
775
|
-
if (perPage !== Number.MAX_SAFE_INTEGER) {
|
|
776
|
-
query += ` LIMIT ? OFFSET ?`;
|
|
777
|
-
queryParams.push(perPage, offset);
|
|
778
|
-
}
|
|
779
|
-
const results = await this.operations.executeQuery({ sql: query, params: queryParams });
|
|
780
|
-
const paginatedMessages = (isArrayOfRecords(results) ? results : []).map((message) => {
|
|
781
|
-
const processedMsg = {};
|
|
782
|
-
for (const [key, value] of Object.entries(message)) {
|
|
783
|
-
if (key === `type` && value === `v2`) continue;
|
|
784
|
-
processedMsg[key] = deserializeValue(value);
|
|
785
|
-
}
|
|
786
|
-
return processedMsg;
|
|
787
|
-
});
|
|
788
|
-
const paginatedCount = paginatedMessages.length;
|
|
789
|
-
let countQuery = `SELECT count() as count FROM ${fullTableName} WHERE thread_id = ?`;
|
|
790
|
-
const countParams = [threadId];
|
|
791
|
-
if (resourceId) {
|
|
792
|
-
countQuery += ` AND resourceId = ?`;
|
|
793
|
-
countParams.push(resourceId);
|
|
794
|
-
}
|
|
795
|
-
if (dateRange?.start) {
|
|
796
|
-
const startDate = dateRange.start instanceof Date ? serializeDate(dateRange.start) : serializeDate(new Date(dateRange.start));
|
|
797
|
-
countQuery += ` AND createdAt >= ?`;
|
|
798
|
-
countParams.push(startDate);
|
|
799
|
-
}
|
|
800
|
-
if (dateRange?.end) {
|
|
801
|
-
const endDate = dateRange.end instanceof Date ? serializeDate(dateRange.end) : serializeDate(new Date(dateRange.end));
|
|
802
|
-
countQuery += ` AND createdAt <= ?`;
|
|
803
|
-
countParams.push(endDate);
|
|
804
|
-
}
|
|
805
|
-
const countResult = await this.operations.executeQuery({ sql: countQuery, params: countParams });
|
|
806
|
-
const total = Number(countResult[0]?.count ?? 0);
|
|
807
|
-
if (total === 0 && paginatedCount === 0 && (!include || include.length === 0)) {
|
|
808
|
-
return {
|
|
809
|
-
messages: [],
|
|
810
|
-
total: 0,
|
|
811
|
-
page,
|
|
812
|
-
perPage: perPageForResponse,
|
|
813
|
-
hasMore: false
|
|
814
|
-
};
|
|
815
|
-
}
|
|
816
|
-
const messageIds = new Set(paginatedMessages.map((m) => m.id));
|
|
817
|
-
let includeMessages = [];
|
|
818
|
-
if (include && include.length > 0) {
|
|
819
|
-
const includeResult = await this._getIncludedMessages(threadId, include);
|
|
820
|
-
if (Array.isArray(includeResult)) {
|
|
821
|
-
includeMessages = includeResult;
|
|
822
|
-
for (const includeMsg of includeMessages) {
|
|
823
|
-
if (!messageIds.has(includeMsg.id)) {
|
|
824
|
-
paginatedMessages.push(includeMsg);
|
|
825
|
-
messageIds.add(includeMsg.id);
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
const list = new MessageList().add(paginatedMessages, "memory");
|
|
831
|
-
let finalMessages = list.get.all.db();
|
|
832
|
-
finalMessages = finalMessages.sort((a, b) => {
|
|
833
|
-
const isDateField = field === "createdAt" || field === "updatedAt";
|
|
834
|
-
const aValue = isDateField ? new Date(a[field]).getTime() : a[field];
|
|
835
|
-
const bValue = isDateField ? new Date(b[field]).getTime() : b[field];
|
|
836
|
-
if (aValue === bValue) {
|
|
837
|
-
return a.id.localeCompare(b.id);
|
|
838
|
-
}
|
|
839
|
-
if (typeof aValue === "number" && typeof bValue === "number") {
|
|
840
|
-
return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
841
|
-
}
|
|
842
|
-
return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
|
|
843
|
-
});
|
|
844
|
-
const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
|
|
845
|
-
const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
|
|
846
|
-
const hasMore = perPageInput === false ? false : allThreadMessagesReturned ? false : offset + paginatedCount < total;
|
|
847
821
|
return {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
hasMore
|
|
822
|
+
...thread,
|
|
823
|
+
createdAt: ensureDate(thread.createdAt),
|
|
824
|
+
updatedAt: ensureDate(thread.updatedAt),
|
|
825
|
+
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata || "{}") : thread.metadata || {}
|
|
853
826
|
};
|
|
854
827
|
} catch (error) {
|
|
855
828
|
const mastraError = new MastraError(
|
|
856
829
|
{
|
|
857
|
-
id: "
|
|
830
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_THREAD_BY_ID", "FAILED"),
|
|
858
831
|
domain: ErrorDomain.STORAGE,
|
|
859
832
|
category: ErrorCategory.THIRD_PARTY,
|
|
860
|
-
text: `
|
|
861
|
-
details: {
|
|
862
|
-
threadId,
|
|
863
|
-
resourceId: resourceId ?? ""
|
|
864
|
-
}
|
|
833
|
+
text: `Error processing thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
834
|
+
details: { threadId }
|
|
865
835
|
},
|
|
866
836
|
error
|
|
867
837
|
);
|
|
868
|
-
this.logger?.error
|
|
869
|
-
this.logger?.trackException
|
|
870
|
-
return
|
|
871
|
-
messages: [],
|
|
872
|
-
total: 0,
|
|
873
|
-
page,
|
|
874
|
-
perPage: perPageForResponse,
|
|
875
|
-
hasMore: false
|
|
876
|
-
};
|
|
838
|
+
this.logger?.error(mastraError.toString());
|
|
839
|
+
this.logger?.trackException(mastraError);
|
|
840
|
+
return null;
|
|
877
841
|
}
|
|
878
842
|
}
|
|
879
|
-
async
|
|
880
|
-
const {
|
|
881
|
-
this.logger.debug("Updating messages", { count: messages.length });
|
|
882
|
-
if (!messages.length) {
|
|
883
|
-
return [];
|
|
884
|
-
}
|
|
885
|
-
const messageIds = messages.map((m) => m.id);
|
|
886
|
-
const fullTableName = this.operations.getTableName(TABLE_MESSAGES);
|
|
887
|
-
const threadsTableName = this.operations.getTableName(TABLE_THREADS);
|
|
843
|
+
async listThreads(args) {
|
|
844
|
+
const { page = 0, perPage: perPageInput, orderBy, filter } = args;
|
|
888
845
|
try {
|
|
889
|
-
|
|
890
|
-
const selectQuery = `SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId FROM ${fullTableName} WHERE id IN (${placeholders})`;
|
|
891
|
-
const existingMessages = await this.operations.executeQuery({ sql: selectQuery, params: messageIds });
|
|
892
|
-
if (existingMessages.length === 0) {
|
|
893
|
-
return [];
|
|
894
|
-
}
|
|
895
|
-
const parsedExistingMessages = existingMessages.map((msg) => {
|
|
896
|
-
if (typeof msg.content === "string") {
|
|
897
|
-
try {
|
|
898
|
-
msg.content = JSON.parse(msg.content);
|
|
899
|
-
} catch {
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
return msg;
|
|
903
|
-
});
|
|
904
|
-
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
905
|
-
const updateQueries = [];
|
|
906
|
-
for (const existingMessage of parsedExistingMessages) {
|
|
907
|
-
const updatePayload = messages.find((m) => m.id === existingMessage.id);
|
|
908
|
-
if (!updatePayload) continue;
|
|
909
|
-
const { id, ...fieldsToUpdate } = updatePayload;
|
|
910
|
-
if (Object.keys(fieldsToUpdate).length === 0) continue;
|
|
911
|
-
threadIdsToUpdate.add(existingMessage.threadId);
|
|
912
|
-
if ("threadId" in updatePayload && updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
|
|
913
|
-
threadIdsToUpdate.add(updatePayload.threadId);
|
|
914
|
-
}
|
|
915
|
-
const setClauses = [];
|
|
916
|
-
const values = [];
|
|
917
|
-
const updatableFields = { ...fieldsToUpdate };
|
|
918
|
-
if (updatableFields.content) {
|
|
919
|
-
const existingContent = existingMessage.content || {};
|
|
920
|
-
const newContent = {
|
|
921
|
-
...existingContent,
|
|
922
|
-
...updatableFields.content,
|
|
923
|
-
// Deep merge metadata if it exists on both
|
|
924
|
-
...existingContent?.metadata && updatableFields.content.metadata ? {
|
|
925
|
-
metadata: {
|
|
926
|
-
...existingContent.metadata,
|
|
927
|
-
...updatableFields.content.metadata
|
|
928
|
-
}
|
|
929
|
-
} : {}
|
|
930
|
-
};
|
|
931
|
-
setClauses.push(`content = ?`);
|
|
932
|
-
values.push(JSON.stringify(newContent));
|
|
933
|
-
delete updatableFields.content;
|
|
934
|
-
}
|
|
935
|
-
for (const key in updatableFields) {
|
|
936
|
-
if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
|
|
937
|
-
const dbColumn = key === "threadId" ? "thread_id" : key;
|
|
938
|
-
setClauses.push(`${dbColumn} = ?`);
|
|
939
|
-
values.push(updatableFields[key]);
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
if (setClauses.length > 0) {
|
|
943
|
-
values.push(id);
|
|
944
|
-
const updateQuery = `UPDATE ${fullTableName} SET ${setClauses.join(", ")} WHERE id = ?`;
|
|
945
|
-
updateQueries.push({ sql: updateQuery, params: values });
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
for (const query of updateQueries) {
|
|
949
|
-
await this.operations.executeQuery(query);
|
|
950
|
-
}
|
|
951
|
-
if (threadIdsToUpdate.size > 0) {
|
|
952
|
-
const threadPlaceholders = Array.from(threadIdsToUpdate).map(() => "?").join(",");
|
|
953
|
-
const threadUpdateQuery = `UPDATE ${threadsTableName} SET updatedAt = ? WHERE id IN (${threadPlaceholders})`;
|
|
954
|
-
const threadUpdateParams = [(/* @__PURE__ */ new Date()).toISOString(), ...Array.from(threadIdsToUpdate)];
|
|
955
|
-
await this.operations.executeQuery({ sql: threadUpdateQuery, params: threadUpdateParams });
|
|
956
|
-
}
|
|
957
|
-
const updatedMessages = await this.operations.executeQuery({ sql: selectQuery, params: messageIds });
|
|
958
|
-
return updatedMessages.map((message) => {
|
|
959
|
-
if (typeof message.content === "string") {
|
|
960
|
-
try {
|
|
961
|
-
message.content = JSON.parse(message.content);
|
|
962
|
-
} catch {
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
return message;
|
|
966
|
-
});
|
|
846
|
+
this.validatePaginationInput(page, perPageInput ?? 100);
|
|
967
847
|
} catch (error) {
|
|
968
848
|
throw new MastraError(
|
|
969
849
|
{
|
|
970
|
-
id: "
|
|
850
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "INVALID_PAGE"),
|
|
971
851
|
domain: ErrorDomain.STORAGE,
|
|
972
|
-
category: ErrorCategory.
|
|
973
|
-
details: {
|
|
974
|
-
},
|
|
975
|
-
error
|
|
976
|
-
);
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
client;
|
|
982
|
-
binding;
|
|
983
|
-
tablePrefix;
|
|
984
|
-
constructor(config) {
|
|
985
|
-
super();
|
|
986
|
-
this.client = config.client;
|
|
987
|
-
this.binding = config.binding;
|
|
988
|
-
this.tablePrefix = config.tablePrefix || "";
|
|
989
|
-
}
|
|
990
|
-
async hasColumn(table, column) {
|
|
991
|
-
const fullTableName = table.startsWith(this.tablePrefix) ? table : `${this.tablePrefix}${table}`;
|
|
992
|
-
const sql = `PRAGMA table_info(${fullTableName});`;
|
|
993
|
-
const result = await this.executeQuery({ sql, params: [] });
|
|
994
|
-
if (!result || !Array.isArray(result)) return false;
|
|
995
|
-
return result.some((col) => col.name === column || col.name === column.toLowerCase());
|
|
996
|
-
}
|
|
997
|
-
getTableName(tableName) {
|
|
998
|
-
return `${this.tablePrefix}${tableName}`;
|
|
999
|
-
}
|
|
1000
|
-
formatSqlParams(params) {
|
|
1001
|
-
return params.map((p) => p === void 0 || p === null ? null : p);
|
|
1002
|
-
}
|
|
1003
|
-
async executeWorkersBindingQuery({
|
|
1004
|
-
sql,
|
|
1005
|
-
params = [],
|
|
1006
|
-
first = false
|
|
1007
|
-
}) {
|
|
1008
|
-
if (!this.binding) {
|
|
1009
|
-
throw new Error("Workers binding is not configured");
|
|
1010
|
-
}
|
|
1011
|
-
try {
|
|
1012
|
-
const statement = this.binding.prepare(sql);
|
|
1013
|
-
const formattedParams = this.formatSqlParams(params);
|
|
1014
|
-
let result;
|
|
1015
|
-
if (formattedParams.length > 0) {
|
|
1016
|
-
if (first) {
|
|
1017
|
-
result = await statement.bind(...formattedParams).first();
|
|
1018
|
-
if (!result) return null;
|
|
1019
|
-
return result;
|
|
1020
|
-
} else {
|
|
1021
|
-
result = await statement.bind(...formattedParams).all();
|
|
1022
|
-
const results = result.results || [];
|
|
1023
|
-
return results;
|
|
1024
|
-
}
|
|
1025
|
-
} else {
|
|
1026
|
-
if (first) {
|
|
1027
|
-
result = await statement.first();
|
|
1028
|
-
if (!result) return null;
|
|
1029
|
-
return result;
|
|
1030
|
-
} else {
|
|
1031
|
-
result = await statement.all();
|
|
1032
|
-
const results = result.results || [];
|
|
1033
|
-
return results;
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
852
|
+
category: ErrorCategory.USER,
|
|
853
|
+
details: { page, ...perPageInput !== void 0 && { perPage: perPageInput } }
|
|
854
|
+
},
|
|
855
|
+
error instanceof Error ? error : new Error("Invalid pagination parameters")
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
859
|
+
try {
|
|
860
|
+
this.validateMetadataKeys(filter?.metadata);
|
|
1036
861
|
} catch (error) {
|
|
1037
862
|
throw new MastraError(
|
|
1038
863
|
{
|
|
1039
|
-
id: "
|
|
864
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "INVALID_METADATA_KEY"),
|
|
1040
865
|
domain: ErrorDomain.STORAGE,
|
|
1041
|
-
category: ErrorCategory.
|
|
1042
|
-
details: {
|
|
866
|
+
category: ErrorCategory.USER,
|
|
867
|
+
details: { metadataKeys: filter?.metadata ? Object.keys(filter.metadata).join(", ") : "" }
|
|
1043
868
|
},
|
|
1044
|
-
error
|
|
869
|
+
error instanceof Error ? error : new Error("Invalid metadata key")
|
|
1045
870
|
);
|
|
1046
871
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
}
|
|
872
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
873
|
+
const { field, direction } = this.parseOrderBy(orderBy);
|
|
874
|
+
const fullTableName = this.#db.getTableName(TABLE_THREADS);
|
|
875
|
+
const mapRowToStorageThreadType = (row) => ({
|
|
876
|
+
...row,
|
|
877
|
+
createdAt: ensureDate(row.createdAt),
|
|
878
|
+
updatedAt: ensureDate(row.updatedAt),
|
|
879
|
+
metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata || "{}") : row.metadata || {}
|
|
880
|
+
});
|
|
1056
881
|
try {
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
const result = response.result || [];
|
|
1063
|
-
const results = result.flatMap((r) => r.results || []);
|
|
1064
|
-
if (first) {
|
|
1065
|
-
return results[0] || null;
|
|
882
|
+
let countQuery = createSqlBuilder().count().from(fullTableName);
|
|
883
|
+
let selectQuery = createSqlBuilder().select("*").from(fullTableName);
|
|
884
|
+
if (filter?.resourceId) {
|
|
885
|
+
countQuery = countQuery.whereAnd("resourceId = ?", filter.resourceId);
|
|
886
|
+
selectQuery = selectQuery.whereAnd("resourceId = ?", filter.resourceId);
|
|
1066
887
|
}
|
|
1067
|
-
|
|
888
|
+
if (filter?.metadata && Object.keys(filter.metadata).length > 0) {
|
|
889
|
+
for (const [key, value] of Object.entries(filter.metadata)) {
|
|
890
|
+
if (value !== null && typeof value === "object") {
|
|
891
|
+
throw new MastraError(
|
|
892
|
+
{
|
|
893
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "INVALID_METADATA_VALUE"),
|
|
894
|
+
domain: ErrorDomain.STORAGE,
|
|
895
|
+
category: ErrorCategory.USER,
|
|
896
|
+
text: `Metadata filter value for key "${key}" must be a scalar type (string, number, boolean, or null), got ${Array.isArray(value) ? "array" : "object"}`,
|
|
897
|
+
details: { key, valueType: Array.isArray(value) ? "array" : "object" }
|
|
898
|
+
},
|
|
899
|
+
new Error("Invalid metadata filter value type")
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
if (value === null) {
|
|
903
|
+
const condition = `json_extract(metadata, '$.${key}') IS NULL`;
|
|
904
|
+
countQuery = countQuery.whereAnd(condition);
|
|
905
|
+
selectQuery = selectQuery.whereAnd(condition);
|
|
906
|
+
} else {
|
|
907
|
+
const condition = `json_extract(metadata, '$.${key}') = ?`;
|
|
908
|
+
const filterValue = value;
|
|
909
|
+
countQuery = countQuery.whereAnd(condition, filterValue);
|
|
910
|
+
selectQuery = selectQuery.whereAnd(condition, filterValue);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
const countResult = await this.#db.executeQuery(countQuery.build());
|
|
915
|
+
const total = Number(countResult?.[0]?.count ?? 0);
|
|
916
|
+
if (total === 0) {
|
|
917
|
+
return {
|
|
918
|
+
threads: [],
|
|
919
|
+
total: 0,
|
|
920
|
+
page,
|
|
921
|
+
perPage: perPageForResponse,
|
|
922
|
+
hasMore: false
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
926
|
+
selectQuery = selectQuery.orderBy(field, direction).limit(limitValue).offset(offset);
|
|
927
|
+
const results = await this.#db.executeQuery(selectQuery.build());
|
|
928
|
+
const threads = results.map(mapRowToStorageThreadType);
|
|
929
|
+
return {
|
|
930
|
+
threads,
|
|
931
|
+
total,
|
|
932
|
+
page,
|
|
933
|
+
perPage: perPageForResponse,
|
|
934
|
+
hasMore: perPageInput === false ? false : offset + perPage < total
|
|
935
|
+
};
|
|
1068
936
|
} catch (error) {
|
|
1069
|
-
|
|
937
|
+
if (error instanceof MastraError && error.category === ErrorCategory.USER) {
|
|
938
|
+
throw error;
|
|
939
|
+
}
|
|
940
|
+
const mastraError = new MastraError(
|
|
1070
941
|
{
|
|
1071
|
-
id: "
|
|
942
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "FAILED"),
|
|
1072
943
|
domain: ErrorDomain.STORAGE,
|
|
1073
944
|
category: ErrorCategory.THIRD_PARTY,
|
|
1074
|
-
|
|
945
|
+
text: `Error listing threads: ${error instanceof Error ? error.message : String(error)}`,
|
|
946
|
+
details: {
|
|
947
|
+
...filter?.resourceId && { resourceId: filter.resourceId },
|
|
948
|
+
hasMetadataFilter: !!filter?.metadata
|
|
949
|
+
}
|
|
1075
950
|
},
|
|
1076
951
|
error
|
|
1077
952
|
);
|
|
953
|
+
this.logger?.error(mastraError.toString());
|
|
954
|
+
this.logger?.trackException(mastraError);
|
|
955
|
+
return {
|
|
956
|
+
threads: [],
|
|
957
|
+
total: 0,
|
|
958
|
+
page,
|
|
959
|
+
perPage: perPageForResponse,
|
|
960
|
+
hasMore: false
|
|
961
|
+
};
|
|
1078
962
|
}
|
|
1079
963
|
}
|
|
1080
|
-
async
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
964
|
+
async saveThread({ thread }) {
|
|
965
|
+
const fullTableName = this.#db.getTableName(TABLE_THREADS);
|
|
966
|
+
const threadToSave = {
|
|
967
|
+
id: thread.id,
|
|
968
|
+
resourceId: thread.resourceId,
|
|
969
|
+
title: thread.title,
|
|
970
|
+
metadata: thread.metadata ? JSON.stringify(thread.metadata) : null,
|
|
971
|
+
createdAt: thread.createdAt.toISOString(),
|
|
972
|
+
updatedAt: thread.updatedAt.toISOString()
|
|
973
|
+
};
|
|
974
|
+
const processedRecord = await this.#db.processRecord(threadToSave);
|
|
975
|
+
const columns = Object.keys(processedRecord);
|
|
976
|
+
const values = Object.values(processedRecord);
|
|
977
|
+
const updateMap = {
|
|
978
|
+
resourceId: "excluded.resourceId",
|
|
979
|
+
title: "excluded.title",
|
|
980
|
+
metadata: "excluded.metadata",
|
|
981
|
+
createdAt: "excluded.createdAt",
|
|
982
|
+
updatedAt: "excluded.updatedAt"
|
|
983
|
+
};
|
|
984
|
+
const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], updateMap);
|
|
985
|
+
const { sql, params } = query.build();
|
|
1090
986
|
try {
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
if (!result || !Array.isArray(result)) {
|
|
1094
|
-
return [];
|
|
1095
|
-
}
|
|
1096
|
-
return result.map((row) => ({
|
|
1097
|
-
name: row.name,
|
|
1098
|
-
type: row.type
|
|
1099
|
-
}));
|
|
987
|
+
await this.#db.executeQuery({ sql, params });
|
|
988
|
+
return thread;
|
|
1100
989
|
} catch (error) {
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
}
|
|
1112
|
-
if (typeof value === "object") {
|
|
1113
|
-
return JSON.stringify(value);
|
|
1114
|
-
}
|
|
1115
|
-
return value;
|
|
1116
|
-
}
|
|
1117
|
-
getSqlType(type) {
|
|
1118
|
-
switch (type) {
|
|
1119
|
-
case "bigint":
|
|
1120
|
-
return "INTEGER";
|
|
1121
|
-
// SQLite uses INTEGER for all integer sizes
|
|
1122
|
-
case "jsonb":
|
|
1123
|
-
return "TEXT";
|
|
1124
|
-
// Store JSON as TEXT in SQLite
|
|
1125
|
-
default:
|
|
1126
|
-
return super.getSqlType(type);
|
|
990
|
+
throw new MastraError(
|
|
991
|
+
{
|
|
992
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_THREAD", "FAILED"),
|
|
993
|
+
domain: ErrorDomain.STORAGE,
|
|
994
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
995
|
+
text: `Failed to save thread to ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
996
|
+
details: { threadId: thread.id }
|
|
997
|
+
},
|
|
998
|
+
error
|
|
999
|
+
);
|
|
1127
1000
|
}
|
|
1128
1001
|
}
|
|
1129
|
-
async
|
|
1130
|
-
|
|
1131
|
-
|
|
1002
|
+
async updateThread({
|
|
1003
|
+
id,
|
|
1004
|
+
title,
|
|
1005
|
+
metadata
|
|
1132
1006
|
}) {
|
|
1007
|
+
const thread = await this.getThreadById({ threadId: id });
|
|
1133
1008
|
try {
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
const type = this.getSqlType(colDef.type);
|
|
1137
|
-
const nullable = colDef.nullable === false ? "NOT NULL" : "";
|
|
1138
|
-
const primaryKey = colDef.primaryKey ? "PRIMARY KEY" : "";
|
|
1139
|
-
return `${colName} ${type} ${nullable} ${primaryKey}`.trim();
|
|
1140
|
-
});
|
|
1141
|
-
const tableConstraints = [];
|
|
1142
|
-
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
1143
|
-
tableConstraints.push("UNIQUE (workflow_name, run_id)");
|
|
1009
|
+
if (!thread) {
|
|
1010
|
+
throw new Error(`Thread ${id} not found`);
|
|
1144
1011
|
}
|
|
1145
|
-
const
|
|
1012
|
+
const fullTableName = this.#db.getTableName(TABLE_THREADS);
|
|
1013
|
+
const mergedMetadata = {
|
|
1014
|
+
...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
1015
|
+
...metadata
|
|
1016
|
+
};
|
|
1017
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
1018
|
+
const columns = ["title", "metadata", "updatedAt"];
|
|
1019
|
+
const values = [title, JSON.stringify(mergedMetadata), updatedAt.toISOString()];
|
|
1020
|
+
const query = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", id);
|
|
1146
1021
|
const { sql, params } = query.build();
|
|
1147
|
-
await this.executeQuery({ sql, params });
|
|
1148
|
-
|
|
1022
|
+
await this.#db.executeQuery({ sql, params });
|
|
1023
|
+
return {
|
|
1024
|
+
...thread,
|
|
1025
|
+
title,
|
|
1026
|
+
metadata: {
|
|
1027
|
+
...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
1028
|
+
...metadata
|
|
1029
|
+
},
|
|
1030
|
+
updatedAt
|
|
1031
|
+
};
|
|
1149
1032
|
} catch (error) {
|
|
1150
1033
|
throw new MastraError(
|
|
1151
1034
|
{
|
|
1152
|
-
id: "
|
|
1035
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "UPDATE_THREAD", "FAILED"),
|
|
1153
1036
|
domain: ErrorDomain.STORAGE,
|
|
1154
1037
|
category: ErrorCategory.THIRD_PARTY,
|
|
1155
|
-
|
|
1038
|
+
text: `Failed to update thread ${id}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1039
|
+
details: { threadId: id }
|
|
1156
1040
|
},
|
|
1157
1041
|
error
|
|
1158
1042
|
);
|
|
1159
1043
|
}
|
|
1160
1044
|
}
|
|
1161
|
-
async
|
|
1045
|
+
async deleteThread({ threadId }) {
|
|
1046
|
+
const fullTableName = this.#db.getTableName(TABLE_THREADS);
|
|
1162
1047
|
try {
|
|
1163
|
-
const
|
|
1164
|
-
const
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1048
|
+
const deleteThreadQuery = createSqlBuilder().delete(fullTableName).where("id = ?", threadId);
|
|
1049
|
+
const { sql: threadSql, params: threadParams } = deleteThreadQuery.build();
|
|
1050
|
+
await this.#db.executeQuery({ sql: threadSql, params: threadParams });
|
|
1051
|
+
const messagesTableName = this.#db.getTableName(TABLE_MESSAGES);
|
|
1052
|
+
const deleteMessagesQuery = createSqlBuilder().delete(messagesTableName).where("thread_id = ?", threadId);
|
|
1053
|
+
const { sql: messagesSql, params: messagesParams } = deleteMessagesQuery.build();
|
|
1054
|
+
await this.#db.executeQuery({ sql: messagesSql, params: messagesParams });
|
|
1168
1055
|
} catch (error) {
|
|
1169
1056
|
throw new MastraError(
|
|
1170
1057
|
{
|
|
1171
|
-
id: "
|
|
1058
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "DELETE_THREAD", "FAILED"),
|
|
1172
1059
|
domain: ErrorDomain.STORAGE,
|
|
1173
1060
|
category: ErrorCategory.THIRD_PARTY,
|
|
1174
|
-
|
|
1061
|
+
text: `Failed to delete thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1062
|
+
details: { threadId }
|
|
1175
1063
|
},
|
|
1176
1064
|
error
|
|
1177
1065
|
);
|
|
1178
1066
|
}
|
|
1179
1067
|
}
|
|
1180
|
-
async
|
|
1068
|
+
async saveMessages(args) {
|
|
1069
|
+
const { messages } = args;
|
|
1070
|
+
if (messages.length === 0) return { messages: [] };
|
|
1181
1071
|
try {
|
|
1182
|
-
const
|
|
1183
|
-
const
|
|
1184
|
-
|
|
1185
|
-
|
|
1072
|
+
const now = /* @__PURE__ */ new Date();
|
|
1073
|
+
const threadId = messages[0]?.threadId;
|
|
1074
|
+
for (const [i, message] of messages.entries()) {
|
|
1075
|
+
if (!message.id) throw new Error(`Message at index ${i} missing id`);
|
|
1076
|
+
if (!message.threadId) {
|
|
1077
|
+
throw new Error(`Message at index ${i} missing threadId`);
|
|
1078
|
+
}
|
|
1079
|
+
if (!message.content) {
|
|
1080
|
+
throw new Error(`Message at index ${i} missing content`);
|
|
1081
|
+
}
|
|
1082
|
+
if (!message.role) {
|
|
1083
|
+
throw new Error(`Message at index ${i} missing role`);
|
|
1084
|
+
}
|
|
1085
|
+
if (!message.resourceId) {
|
|
1086
|
+
throw new Error(`Message at index ${i} missing resourceId`);
|
|
1087
|
+
}
|
|
1088
|
+
const thread = await this.getThreadById({ threadId: message.threadId });
|
|
1089
|
+
if (!thread) {
|
|
1090
|
+
throw new Error(`Thread ${message.threadId} not found`);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
const messagesToInsert = messages.map((message) => {
|
|
1094
|
+
const createdAt = message.createdAt ? new Date(message.createdAt) : now;
|
|
1095
|
+
return {
|
|
1096
|
+
id: message.id,
|
|
1097
|
+
thread_id: message.threadId,
|
|
1098
|
+
content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
|
|
1099
|
+
createdAt: createdAt.toISOString(),
|
|
1100
|
+
role: message.role,
|
|
1101
|
+
type: message.type || "v2",
|
|
1102
|
+
resourceId: message.resourceId
|
|
1103
|
+
};
|
|
1104
|
+
});
|
|
1105
|
+
await Promise.all([
|
|
1106
|
+
this.#db.batchUpsert({
|
|
1107
|
+
tableName: TABLE_MESSAGES,
|
|
1108
|
+
records: messagesToInsert
|
|
1109
|
+
}),
|
|
1110
|
+
// Update thread's updatedAt timestamp
|
|
1111
|
+
this.#db.executeQuery({
|
|
1112
|
+
sql: `UPDATE ${this.#db.getTableName(TABLE_THREADS)} SET updatedAt = ? WHERE id = ?`,
|
|
1113
|
+
params: [now.toISOString(), threadId]
|
|
1114
|
+
})
|
|
1115
|
+
]);
|
|
1116
|
+
this.logger.debug(`Saved ${messages.length} messages`);
|
|
1117
|
+
const list = new MessageList().add(messages, "memory");
|
|
1118
|
+
return { messages: list.get.all.db() };
|
|
1186
1119
|
} catch (error) {
|
|
1187
1120
|
throw new MastraError(
|
|
1188
1121
|
{
|
|
1189
|
-
id: "
|
|
1122
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_MESSAGES", "FAILED"),
|
|
1190
1123
|
domain: ErrorDomain.STORAGE,
|
|
1191
1124
|
category: ErrorCategory.THIRD_PARTY,
|
|
1192
|
-
|
|
1125
|
+
text: `Failed to save messages: ${error instanceof Error ? error.message : String(error)}`
|
|
1193
1126
|
},
|
|
1194
1127
|
error
|
|
1195
1128
|
);
|
|
1196
1129
|
}
|
|
1197
1130
|
}
|
|
1198
|
-
async
|
|
1131
|
+
async _getIncludedMessages(include) {
|
|
1132
|
+
if (!include || include.length === 0) return null;
|
|
1133
|
+
const unionQueries = [];
|
|
1134
|
+
const params = [];
|
|
1135
|
+
let paramIdx = 1;
|
|
1136
|
+
const tableName = this.#db.getTableName(TABLE_MESSAGES);
|
|
1137
|
+
for (const inc of include) {
|
|
1138
|
+
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
1139
|
+
unionQueries.push(`
|
|
1140
|
+
SELECT * FROM (
|
|
1141
|
+
WITH target_thread AS (
|
|
1142
|
+
SELECT thread_id FROM ${tableName} WHERE id = ?
|
|
1143
|
+
),
|
|
1144
|
+
ordered_messages AS (
|
|
1145
|
+
SELECT
|
|
1146
|
+
*,
|
|
1147
|
+
ROW_NUMBER() OVER (ORDER BY createdAt ASC) AS row_num
|
|
1148
|
+
FROM ${tableName}
|
|
1149
|
+
WHERE thread_id = (SELECT thread_id FROM target_thread)
|
|
1150
|
+
)
|
|
1151
|
+
SELECT
|
|
1152
|
+
m.id,
|
|
1153
|
+
m.content,
|
|
1154
|
+
m.role,
|
|
1155
|
+
m.type,
|
|
1156
|
+
m.createdAt,
|
|
1157
|
+
m.thread_id AS threadId,
|
|
1158
|
+
m.resourceId
|
|
1159
|
+
FROM ordered_messages m
|
|
1160
|
+
WHERE m.id = ?
|
|
1161
|
+
OR EXISTS (
|
|
1162
|
+
SELECT 1 FROM ordered_messages target
|
|
1163
|
+
WHERE target.id = ?
|
|
1164
|
+
AND (
|
|
1165
|
+
(m.row_num <= target.row_num + ? AND m.row_num > target.row_num)
|
|
1166
|
+
OR
|
|
1167
|
+
(m.row_num >= target.row_num - ? AND m.row_num < target.row_num)
|
|
1168
|
+
)
|
|
1169
|
+
)
|
|
1170
|
+
) AS query_${paramIdx}
|
|
1171
|
+
`);
|
|
1172
|
+
params.push(id, id, id, withNextMessages, withPreviousMessages);
|
|
1173
|
+
paramIdx++;
|
|
1174
|
+
}
|
|
1175
|
+
const finalQuery = unionQueries.join(" UNION ALL ") + " ORDER BY createdAt ASC";
|
|
1176
|
+
const messages = await this.#db.executeQuery({ sql: finalQuery, params });
|
|
1177
|
+
if (!Array.isArray(messages)) {
|
|
1178
|
+
return [];
|
|
1179
|
+
}
|
|
1180
|
+
const processedMessages = messages.map((message) => {
|
|
1181
|
+
const processedMsg = {};
|
|
1182
|
+
for (const [key, value] of Object.entries(message)) {
|
|
1183
|
+
if (key === `type` && value === `v2`) continue;
|
|
1184
|
+
processedMsg[key] = deserializeValue(value);
|
|
1185
|
+
}
|
|
1186
|
+
return processedMsg;
|
|
1187
|
+
});
|
|
1188
|
+
return processedMessages;
|
|
1189
|
+
}
|
|
1190
|
+
async listMessagesById({ messageIds }) {
|
|
1191
|
+
if (messageIds.length === 0) return { messages: [] };
|
|
1192
|
+
const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
|
|
1193
|
+
const messages = [];
|
|
1199
1194
|
try {
|
|
1200
|
-
const
|
|
1201
|
-
|
|
1202
|
-
const
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1195
|
+
const query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId", "resourceId"]).from(fullTableName).where(`id in (${messageIds.map(() => "?").join(",")})`, ...messageIds);
|
|
1196
|
+
query.orderBy("createdAt", "DESC");
|
|
1197
|
+
const { sql, params } = query.build();
|
|
1198
|
+
const result = await this.#db.executeQuery({ sql, params });
|
|
1199
|
+
if (Array.isArray(result)) messages.push(...result);
|
|
1200
|
+
const processedMessages = messages.map((message) => {
|
|
1201
|
+
const processedMsg = {};
|
|
1202
|
+
for (const [key, value] of Object.entries(message)) {
|
|
1203
|
+
if (key === `type` && value === `v2`) continue;
|
|
1204
|
+
processedMsg[key] = deserializeValue(value);
|
|
1210
1205
|
}
|
|
1211
|
-
|
|
1206
|
+
return processedMsg;
|
|
1207
|
+
});
|
|
1208
|
+
this.logger.debug(`Retrieved ${messages.length} messages`);
|
|
1209
|
+
const list = new MessageList().add(processedMessages, "memory");
|
|
1210
|
+
return { messages: list.get.all.db() };
|
|
1212
1211
|
} catch (error) {
|
|
1213
|
-
|
|
1212
|
+
const mastraError = new MastraError(
|
|
1214
1213
|
{
|
|
1215
|
-
id: "
|
|
1214
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES_BY_ID", "FAILED"),
|
|
1216
1215
|
domain: ErrorDomain.STORAGE,
|
|
1217
1216
|
category: ErrorCategory.THIRD_PARTY,
|
|
1218
|
-
|
|
1217
|
+
text: `Failed to retrieve messages by ID: ${error instanceof Error ? error.message : String(error)}`,
|
|
1218
|
+
details: { messageIds: JSON.stringify(messageIds) }
|
|
1219
1219
|
},
|
|
1220
1220
|
error
|
|
1221
1221
|
);
|
|
1222
|
+
this.logger?.error(mastraError.toString());
|
|
1223
|
+
this.logger?.trackException(mastraError);
|
|
1224
|
+
throw mastraError;
|
|
1222
1225
|
}
|
|
1223
1226
|
}
|
|
1224
|
-
async
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
const columns = Object.keys(processedRecord);
|
|
1229
|
-
const values = Object.values(processedRecord);
|
|
1230
|
-
const query = createSqlBuilder().insert(fullTableName, columns, values);
|
|
1231
|
-
const { sql, params } = query.build();
|
|
1232
|
-
await this.executeQuery({ sql, params });
|
|
1233
|
-
} catch (error) {
|
|
1227
|
+
async listMessages(args) {
|
|
1228
|
+
const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
|
|
1229
|
+
const threadIds = Array.isArray(threadId) ? threadId : [threadId];
|
|
1230
|
+
if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) {
|
|
1234
1231
|
throw new MastraError(
|
|
1235
1232
|
{
|
|
1236
|
-
id: "
|
|
1233
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "INVALID_THREAD_ID"),
|
|
1237
1234
|
domain: ErrorDomain.STORAGE,
|
|
1238
1235
|
category: ErrorCategory.THIRD_PARTY,
|
|
1239
|
-
details: {
|
|
1236
|
+
details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
|
|
1240
1237
|
},
|
|
1241
|
-
|
|
1238
|
+
new Error("threadId must be a non-empty string or array of non-empty strings")
|
|
1242
1239
|
);
|
|
1243
1240
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1241
|
+
if (page < 0) {
|
|
1242
|
+
throw new MastraError(
|
|
1243
|
+
{
|
|
1244
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "INVALID_PAGE"),
|
|
1245
|
+
domain: ErrorDomain.STORAGE,
|
|
1246
|
+
category: ErrorCategory.USER,
|
|
1247
|
+
details: { page }
|
|
1248
|
+
},
|
|
1249
|
+
new Error("page must be >= 0")
|
|
1250
|
+
);
|
|
1251
|
+
}
|
|
1252
|
+
const perPage = normalizePerPage(perPageInput, 40);
|
|
1253
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1246
1254
|
try {
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1255
|
+
const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
|
|
1256
|
+
let query = `
|
|
1257
|
+
SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId
|
|
1258
|
+
FROM ${fullTableName}
|
|
1259
|
+
WHERE thread_id = ?
|
|
1260
|
+
`;
|
|
1261
|
+
const queryParams = [threadId];
|
|
1262
|
+
if (resourceId) {
|
|
1263
|
+
query += ` AND resourceId = ?`;
|
|
1264
|
+
queryParams.push(resourceId);
|
|
1265
|
+
}
|
|
1266
|
+
const dateRange = filter?.dateRange;
|
|
1267
|
+
if (dateRange?.start) {
|
|
1268
|
+
const startDate = dateRange.start instanceof Date ? serializeDate(dateRange.start) : serializeDate(new Date(dateRange.start));
|
|
1269
|
+
const startOp = dateRange.startExclusive ? ">" : ">=";
|
|
1270
|
+
query += ` AND createdAt ${startOp} ?`;
|
|
1271
|
+
queryParams.push(startDate);
|
|
1272
|
+
}
|
|
1273
|
+
if (dateRange?.end) {
|
|
1274
|
+
const endDate = dateRange.end instanceof Date ? serializeDate(dateRange.end) : serializeDate(new Date(dateRange.end));
|
|
1275
|
+
const endOp = dateRange.endExclusive ? "<" : "<=";
|
|
1276
|
+
query += ` AND createdAt ${endOp} ?`;
|
|
1277
|
+
queryParams.push(endDate);
|
|
1278
|
+
}
|
|
1279
|
+
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
1280
|
+
query += ` ORDER BY "${field}" ${direction}`;
|
|
1281
|
+
if (perPage !== Number.MAX_SAFE_INTEGER) {
|
|
1282
|
+
query += ` LIMIT ? OFFSET ?`;
|
|
1283
|
+
queryParams.push(perPage, offset);
|
|
1284
|
+
}
|
|
1285
|
+
const results = await this.#db.executeQuery({ sql: query, params: queryParams });
|
|
1286
|
+
const paginatedMessages = (isArrayOfRecords(results) ? results : []).map((message) => {
|
|
1287
|
+
const processedMsg = {};
|
|
1288
|
+
for (const [key, value] of Object.entries(message)) {
|
|
1289
|
+
if (key === `type` && value === `v2`) continue;
|
|
1290
|
+
processedMsg[key] = deserializeValue(value);
|
|
1291
|
+
}
|
|
1292
|
+
return processedMsg;
|
|
1293
|
+
});
|
|
1294
|
+
const paginatedCount = paginatedMessages.length;
|
|
1295
|
+
let countQuery = `SELECT count() as count FROM ${fullTableName} WHERE thread_id = ?`;
|
|
1296
|
+
const countParams = [threadId];
|
|
1297
|
+
if (resourceId) {
|
|
1298
|
+
countQuery += ` AND resourceId = ?`;
|
|
1299
|
+
countParams.push(resourceId);
|
|
1300
|
+
}
|
|
1301
|
+
if (dateRange?.start) {
|
|
1302
|
+
const startDate = dateRange.start instanceof Date ? serializeDate(dateRange.start) : serializeDate(new Date(dateRange.start));
|
|
1303
|
+
const startOp = dateRange.startExclusive ? ">" : ">=";
|
|
1304
|
+
countQuery += ` AND createdAt ${startOp} ?`;
|
|
1305
|
+
countParams.push(startDate);
|
|
1306
|
+
}
|
|
1307
|
+
if (dateRange?.end) {
|
|
1308
|
+
const endDate = dateRange.end instanceof Date ? serializeDate(dateRange.end) : serializeDate(new Date(dateRange.end));
|
|
1309
|
+
const endOp = dateRange.endExclusive ? "<" : "<=";
|
|
1310
|
+
countQuery += ` AND createdAt ${endOp} ?`;
|
|
1311
|
+
countParams.push(endDate);
|
|
1312
|
+
}
|
|
1313
|
+
const countResult = await this.#db.executeQuery({ sql: countQuery, params: countParams });
|
|
1314
|
+
const total = Number(countResult[0]?.count ?? 0);
|
|
1315
|
+
if (total === 0 && paginatedCount === 0 && (!include || include.length === 0)) {
|
|
1316
|
+
return {
|
|
1317
|
+
messages: [],
|
|
1318
|
+
total: 0,
|
|
1319
|
+
page,
|
|
1320
|
+
perPage: perPageForResponse,
|
|
1321
|
+
hasMore: false
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
const messageIds = new Set(paginatedMessages.map((m) => m.id));
|
|
1325
|
+
let includeMessages = [];
|
|
1326
|
+
if (include && include.length > 0) {
|
|
1327
|
+
const includeResult = await this._getIncludedMessages(include);
|
|
1328
|
+
if (Array.isArray(includeResult)) {
|
|
1329
|
+
includeMessages = includeResult;
|
|
1330
|
+
for (const includeMsg of includeMessages) {
|
|
1331
|
+
if (!messageIds.has(includeMsg.id)) {
|
|
1332
|
+
paginatedMessages.push(includeMsg);
|
|
1333
|
+
messageIds.add(includeMsg.id);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1256
1337
|
}
|
|
1338
|
+
const list = new MessageList().add(paginatedMessages, "memory");
|
|
1339
|
+
let finalMessages = list.get.all.db();
|
|
1340
|
+
finalMessages = finalMessages.sort((a, b) => {
|
|
1341
|
+
const isDateField = field === "createdAt" || field === "updatedAt";
|
|
1342
|
+
const aValue = isDateField ? new Date(a[field]).getTime() : a[field];
|
|
1343
|
+
const bValue = isDateField ? new Date(b[field]).getTime() : b[field];
|
|
1344
|
+
if (aValue === bValue) {
|
|
1345
|
+
return a.id.localeCompare(b.id);
|
|
1346
|
+
}
|
|
1347
|
+
if (typeof aValue === "number" && typeof bValue === "number") {
|
|
1348
|
+
return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
1349
|
+
}
|
|
1350
|
+
return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
|
|
1351
|
+
});
|
|
1352
|
+
const returnedThreadMessageIds = new Set(finalMessages.filter((m) => m.threadId === threadId).map((m) => m.id));
|
|
1353
|
+
const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
|
|
1354
|
+
const hasMore = perPageInput === false ? false : allThreadMessagesReturned ? false : offset + paginatedCount < total;
|
|
1355
|
+
return {
|
|
1356
|
+
messages: finalMessages,
|
|
1357
|
+
total,
|
|
1358
|
+
page,
|
|
1359
|
+
perPage: perPageForResponse,
|
|
1360
|
+
hasMore
|
|
1361
|
+
};
|
|
1257
1362
|
} catch (error) {
|
|
1258
|
-
|
|
1363
|
+
const mastraError = new MastraError(
|
|
1259
1364
|
{
|
|
1260
|
-
id: "
|
|
1365
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "FAILED"),
|
|
1261
1366
|
domain: ErrorDomain.STORAGE,
|
|
1262
1367
|
category: ErrorCategory.THIRD_PARTY,
|
|
1263
|
-
|
|
1368
|
+
text: `Failed to list messages for thread ${Array.isArray(threadId) ? threadId.join(",") : threadId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1369
|
+
details: {
|
|
1370
|
+
threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
|
|
1371
|
+
resourceId: resourceId ?? ""
|
|
1372
|
+
}
|
|
1264
1373
|
},
|
|
1265
1374
|
error
|
|
1266
1375
|
);
|
|
1376
|
+
this.logger?.error?.(mastraError.toString());
|
|
1377
|
+
this.logger?.trackException?.(mastraError);
|
|
1378
|
+
return {
|
|
1379
|
+
messages: [],
|
|
1380
|
+
total: 0,
|
|
1381
|
+
page,
|
|
1382
|
+
perPage: perPageForResponse,
|
|
1383
|
+
hasMore: false
|
|
1384
|
+
};
|
|
1267
1385
|
}
|
|
1268
1386
|
}
|
|
1269
|
-
async
|
|
1387
|
+
async updateMessages(args) {
|
|
1388
|
+
const { messages } = args;
|
|
1389
|
+
this.logger.debug("Updating messages", { count: messages.length });
|
|
1390
|
+
if (!messages.length) {
|
|
1391
|
+
return [];
|
|
1392
|
+
}
|
|
1393
|
+
const messageIds = messages.map((m) => m.id);
|
|
1394
|
+
const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
|
|
1395
|
+
const threadsTableName = this.#db.getTableName(TABLE_THREADS);
|
|
1270
1396
|
try {
|
|
1271
|
-
const
|
|
1272
|
-
const
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1397
|
+
const placeholders = messageIds.map(() => "?").join(",");
|
|
1398
|
+
const selectQuery = `SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId FROM ${fullTableName} WHERE id IN (${placeholders})`;
|
|
1399
|
+
const existingMessages = await this.#db.executeQuery({ sql: selectQuery, params: messageIds });
|
|
1400
|
+
if (existingMessages.length === 0) {
|
|
1401
|
+
return [];
|
|
1402
|
+
}
|
|
1403
|
+
const parsedExistingMessages = existingMessages.map((msg) => {
|
|
1404
|
+
if (typeof msg.content === "string") {
|
|
1405
|
+
try {
|
|
1406
|
+
msg.content = JSON.parse(msg.content);
|
|
1407
|
+
} catch {
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
return msg;
|
|
1411
|
+
});
|
|
1412
|
+
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
1413
|
+
const updateQueries = [];
|
|
1414
|
+
for (const existingMessage of parsedExistingMessages) {
|
|
1415
|
+
const updatePayload = messages.find((m) => m.id === existingMessage.id);
|
|
1416
|
+
if (!updatePayload) continue;
|
|
1417
|
+
const { id, ...fieldsToUpdate } = updatePayload;
|
|
1418
|
+
if (Object.keys(fieldsToUpdate).length === 0) continue;
|
|
1419
|
+
threadIdsToUpdate.add(existingMessage.threadId);
|
|
1420
|
+
if ("threadId" in updatePayload && updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
|
|
1421
|
+
threadIdsToUpdate.add(updatePayload.threadId);
|
|
1422
|
+
}
|
|
1423
|
+
const setClauses = [];
|
|
1424
|
+
const values = [];
|
|
1425
|
+
const updatableFields = { ...fieldsToUpdate };
|
|
1426
|
+
if (updatableFields.content) {
|
|
1427
|
+
const existingContent = existingMessage.content || {};
|
|
1428
|
+
const newContent = {
|
|
1429
|
+
...existingContent,
|
|
1430
|
+
...updatableFields.content,
|
|
1431
|
+
// Deep merge metadata if it exists on both
|
|
1432
|
+
...existingContent?.metadata && updatableFields.content.metadata ? {
|
|
1433
|
+
metadata: {
|
|
1434
|
+
...existingContent.metadata,
|
|
1435
|
+
...updatableFields.content.metadata
|
|
1436
|
+
}
|
|
1437
|
+
} : {}
|
|
1438
|
+
};
|
|
1439
|
+
setClauses.push(`content = ?`);
|
|
1440
|
+
values.push(JSON.stringify(newContent));
|
|
1441
|
+
delete updatableFields.content;
|
|
1442
|
+
}
|
|
1443
|
+
for (const key in updatableFields) {
|
|
1444
|
+
if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
|
|
1445
|
+
const dbColumn = key === "threadId" ? "thread_id" : key;
|
|
1446
|
+
setClauses.push(`${dbColumn} = ?`);
|
|
1447
|
+
values.push(updatableFields[key]);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
if (setClauses.length > 0) {
|
|
1451
|
+
values.push(id);
|
|
1452
|
+
const updateQuery = `UPDATE ${fullTableName} SET ${setClauses.join(", ")} WHERE id = ?`;
|
|
1453
|
+
updateQueries.push({ sql: updateQuery, params: values });
|
|
1280
1454
|
}
|
|
1281
1455
|
}
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
const { sql, params } = query.build();
|
|
1285
|
-
const result = await this.executeQuery({ sql, params, first: true });
|
|
1286
|
-
if (!result) {
|
|
1287
|
-
return null;
|
|
1456
|
+
for (const query of updateQueries) {
|
|
1457
|
+
await this.#db.executeQuery(query);
|
|
1288
1458
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1459
|
+
if (threadIdsToUpdate.size > 0) {
|
|
1460
|
+
const threadPlaceholders = Array.from(threadIdsToUpdate).map(() => "?").join(",");
|
|
1461
|
+
const threadUpdateQuery = `UPDATE ${threadsTableName} SET updatedAt = ? WHERE id IN (${threadPlaceholders})`;
|
|
1462
|
+
const threadUpdateParams = [(/* @__PURE__ */ new Date()).toISOString(), ...Array.from(threadIdsToUpdate)];
|
|
1463
|
+
await this.#db.executeQuery({ sql: threadUpdateQuery, params: threadUpdateParams });
|
|
1292
1464
|
}
|
|
1293
|
-
|
|
1465
|
+
const updatedMessages = await this.#db.executeQuery({ sql: selectQuery, params: messageIds });
|
|
1466
|
+
return updatedMessages.map((message) => {
|
|
1467
|
+
if (typeof message.content === "string") {
|
|
1468
|
+
try {
|
|
1469
|
+
message.content = JSON.parse(message.content);
|
|
1470
|
+
} catch {
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
return message;
|
|
1474
|
+
});
|
|
1294
1475
|
} catch (error) {
|
|
1295
1476
|
throw new MastraError(
|
|
1296
1477
|
{
|
|
1297
|
-
id: "
|
|
1478
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "UPDATE_MESSAGES", "FAILED"),
|
|
1298
1479
|
domain: ErrorDomain.STORAGE,
|
|
1299
1480
|
category: ErrorCategory.THIRD_PARTY,
|
|
1300
|
-
details: {
|
|
1481
|
+
details: { count: messages.length }
|
|
1301
1482
|
},
|
|
1302
1483
|
error
|
|
1303
1484
|
);
|
|
1304
1485
|
}
|
|
1305
1486
|
}
|
|
1306
|
-
async
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
}
|
|
1311
|
-
return processed;
|
|
1312
|
-
}
|
|
1313
|
-
/**
|
|
1314
|
-
* Upsert multiple records in a batch operation
|
|
1315
|
-
* @param tableName The table to insert into
|
|
1316
|
-
* @param records The records to insert
|
|
1317
|
-
*/
|
|
1318
|
-
async batchUpsert({ tableName, records }) {
|
|
1319
|
-
if (records.length === 0) return;
|
|
1320
|
-
const fullTableName = this.getTableName(tableName);
|
|
1487
|
+
async deleteMessages(messageIds) {
|
|
1488
|
+
if (messageIds.length === 0) return;
|
|
1489
|
+
const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
|
|
1490
|
+
const threadsTableName = this.#db.getTableName(TABLE_THREADS);
|
|
1321
1491
|
try {
|
|
1322
|
-
const
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
});
|
|
1335
|
-
const recordToUpsert = columns.reduce(
|
|
1336
|
-
(acc, col) => {
|
|
1337
|
-
if (col !== "createdAt") acc[col] = `excluded.${col}`;
|
|
1338
|
-
return acc;
|
|
1339
|
-
},
|
|
1340
|
-
{}
|
|
1341
|
-
);
|
|
1342
|
-
const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], recordToUpsert);
|
|
1343
|
-
const { sql, params } = query.build();
|
|
1344
|
-
await this.executeQuery({ sql, params });
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
this.logger.debug(
|
|
1348
|
-
`Processed batch ${Math.floor(i / batchSize) + 1} of ${Math.ceil(records.length / batchSize)}`
|
|
1349
|
-
);
|
|
1492
|
+
const placeholders = messageIds.map(() => "?").join(",");
|
|
1493
|
+
const selectQuery = `SELECT DISTINCT thread_id FROM ${fullTableName} WHERE id IN (${placeholders})`;
|
|
1494
|
+
const threadResults = await this.#db.executeQuery({ sql: selectQuery, params: messageIds });
|
|
1495
|
+
const threadIds = threadResults.map((r) => r.thread_id).filter(Boolean);
|
|
1496
|
+
const deleteQuery = createSqlBuilder().delete(fullTableName).where(`id IN (${placeholders})`, ...messageIds);
|
|
1497
|
+
const { sql, params } = deleteQuery.build();
|
|
1498
|
+
await this.#db.executeQuery({ sql, params });
|
|
1499
|
+
if (threadIds.length > 0) {
|
|
1500
|
+
const threadPlaceholders = threadIds.map(() => "?").join(",");
|
|
1501
|
+
const threadUpdateQuery = `UPDATE ${threadsTableName} SET updatedAt = ? WHERE id IN (${threadPlaceholders})`;
|
|
1502
|
+
const threadUpdateParams = [(/* @__PURE__ */ new Date()).toISOString(), ...threadIds];
|
|
1503
|
+
await this.#db.executeQuery({ sql: threadUpdateQuery, params: threadUpdateParams });
|
|
1350
1504
|
}
|
|
1351
|
-
this.logger.debug(`Successfully batch upserted ${records.length} records into ${tableName}`);
|
|
1352
1505
|
} catch (error) {
|
|
1353
1506
|
throw new MastraError(
|
|
1354
1507
|
{
|
|
1355
|
-
id: "
|
|
1508
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "DELETE_MESSAGES", "FAILED"),
|
|
1356
1509
|
domain: ErrorDomain.STORAGE,
|
|
1357
1510
|
category: ErrorCategory.THIRD_PARTY,
|
|
1358
|
-
|
|
1359
|
-
details: { tableName }
|
|
1511
|
+
details: { messageIds: JSON.stringify(messageIds) }
|
|
1360
1512
|
},
|
|
1361
1513
|
error
|
|
1362
1514
|
);
|
|
@@ -1364,32 +1516,31 @@ var StoreOperationsD1 = class extends StoreOperations {
|
|
|
1364
1516
|
}
|
|
1365
1517
|
};
|
|
1366
1518
|
function transformScoreRow(row) {
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
deserialized.metadata = safelyParseJSON(row.metadata);
|
|
1374
|
-
deserialized.additionalContext = safelyParseJSON(row.additionalContext);
|
|
1375
|
-
deserialized.requestContext = safelyParseJSON(row.requestContext);
|
|
1376
|
-
deserialized.entity = safelyParseJSON(row.entity);
|
|
1377
|
-
deserialized.createdAt = row.createdAtZ || row.createdAt;
|
|
1378
|
-
deserialized.updatedAt = row.updatedAtZ || row.updatedAt;
|
|
1379
|
-
return deserialized;
|
|
1519
|
+
return transformScoreRow$1(row, {
|
|
1520
|
+
preferredTimestampFields: {
|
|
1521
|
+
createdAt: "createdAtZ",
|
|
1522
|
+
updatedAt: "updatedAtZ"
|
|
1523
|
+
}
|
|
1524
|
+
});
|
|
1380
1525
|
}
|
|
1381
1526
|
var ScoresStorageD1 = class extends ScoresStorage {
|
|
1382
|
-
|
|
1383
|
-
constructor(
|
|
1527
|
+
#db;
|
|
1528
|
+
constructor(config) {
|
|
1384
1529
|
super();
|
|
1385
|
-
this
|
|
1530
|
+
this.#db = new D1DB(resolveD1Config(config));
|
|
1531
|
+
}
|
|
1532
|
+
async init() {
|
|
1533
|
+
await this.#db.createTable({ tableName: TABLE_SCORERS, schema: TABLE_SCHEMAS[TABLE_SCORERS] });
|
|
1534
|
+
}
|
|
1535
|
+
async dangerouslyClearAll() {
|
|
1536
|
+
await this.#db.clearTable({ tableName: TABLE_SCORERS });
|
|
1386
1537
|
}
|
|
1387
1538
|
async getScoreById({ id }) {
|
|
1388
1539
|
try {
|
|
1389
|
-
const fullTableName = this.
|
|
1540
|
+
const fullTableName = this.#db.getTableName(TABLE_SCORERS);
|
|
1390
1541
|
const query = createSqlBuilder().select("*").from(fullTableName).where("id = ?", id);
|
|
1391
1542
|
const { sql, params } = query.build();
|
|
1392
|
-
const result = await this.
|
|
1543
|
+
const result = await this.#db.executeQuery({ sql, params, first: true });
|
|
1393
1544
|
if (!result) {
|
|
1394
1545
|
return null;
|
|
1395
1546
|
}
|
|
@@ -1397,7 +1548,7 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1397
1548
|
} catch (error) {
|
|
1398
1549
|
throw new MastraError(
|
|
1399
1550
|
{
|
|
1400
|
-
id: "
|
|
1551
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORE_BY_ID", "FAILED"),
|
|
1401
1552
|
domain: ErrorDomain.STORAGE,
|
|
1402
1553
|
category: ErrorCategory.THIRD_PARTY
|
|
1403
1554
|
},
|
|
@@ -1412,17 +1563,23 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1412
1563
|
} catch (error) {
|
|
1413
1564
|
throw new MastraError(
|
|
1414
1565
|
{
|
|
1415
|
-
id: "
|
|
1566
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_SCORE", "VALIDATION_FAILED"),
|
|
1416
1567
|
domain: ErrorDomain.STORAGE,
|
|
1417
1568
|
category: ErrorCategory.USER,
|
|
1418
|
-
details: {
|
|
1569
|
+
details: {
|
|
1570
|
+
scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
|
|
1571
|
+
entityId: score.entityId ?? "unknown",
|
|
1572
|
+
entityType: score.entityType ?? "unknown",
|
|
1573
|
+
traceId: score.traceId ?? "",
|
|
1574
|
+
spanId: score.spanId ?? ""
|
|
1575
|
+
}
|
|
1419
1576
|
},
|
|
1420
1577
|
error
|
|
1421
1578
|
);
|
|
1422
1579
|
}
|
|
1580
|
+
const id = crypto.randomUUID();
|
|
1423
1581
|
try {
|
|
1424
|
-
const
|
|
1425
|
-
const fullTableName = this.operations.getTableName(TABLE_SCORERS);
|
|
1582
|
+
const fullTableName = this.#db.getTableName(TABLE_SCORERS);
|
|
1426
1583
|
const serializedRecord = {};
|
|
1427
1584
|
for (const [key, value] of Object.entries(parsedScore)) {
|
|
1428
1585
|
if (value !== null && value !== void 0) {
|
|
@@ -1435,22 +1592,23 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1435
1592
|
serializedRecord[key] = null;
|
|
1436
1593
|
}
|
|
1437
1594
|
}
|
|
1595
|
+
const now = /* @__PURE__ */ new Date();
|
|
1438
1596
|
serializedRecord.id = id;
|
|
1439
|
-
serializedRecord.createdAt =
|
|
1440
|
-
serializedRecord.updatedAt =
|
|
1597
|
+
serializedRecord.createdAt = now.toISOString();
|
|
1598
|
+
serializedRecord.updatedAt = now.toISOString();
|
|
1441
1599
|
const columns = Object.keys(serializedRecord);
|
|
1442
1600
|
const values = Object.values(serializedRecord);
|
|
1443
1601
|
const query = createSqlBuilder().insert(fullTableName, columns, values);
|
|
1444
1602
|
const { sql, params } = query.build();
|
|
1445
|
-
await this.
|
|
1446
|
-
|
|
1447
|
-
return { score: scoreFromDb };
|
|
1603
|
+
await this.#db.executeQuery({ sql, params });
|
|
1604
|
+
return { score: { ...parsedScore, id, createdAt: now, updatedAt: now } };
|
|
1448
1605
|
} catch (error) {
|
|
1449
1606
|
throw new MastraError(
|
|
1450
1607
|
{
|
|
1451
|
-
id: "
|
|
1608
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_SCORE", "FAILED"),
|
|
1452
1609
|
domain: ErrorDomain.STORAGE,
|
|
1453
|
-
category: ErrorCategory.THIRD_PARTY
|
|
1610
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1611
|
+
details: { id }
|
|
1454
1612
|
},
|
|
1455
1613
|
error
|
|
1456
1614
|
);
|
|
@@ -1467,7 +1625,7 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1467
1625
|
const { page, perPage: perPageInput } = pagination;
|
|
1468
1626
|
const perPage = normalizePerPage(perPageInput, 100);
|
|
1469
1627
|
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1470
|
-
const fullTableName = this.
|
|
1628
|
+
const fullTableName = this.#db.getTableName(TABLE_SCORERS);
|
|
1471
1629
|
const countQuery = createSqlBuilder().count().from(fullTableName).where("scorerId = ?", scorerId);
|
|
1472
1630
|
if (entityId) {
|
|
1473
1631
|
countQuery.andWhere("entityId = ?", entityId);
|
|
@@ -1478,7 +1636,7 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1478
1636
|
if (source) {
|
|
1479
1637
|
countQuery.andWhere("source = ?", source);
|
|
1480
1638
|
}
|
|
1481
|
-
const countResult = await this.
|
|
1639
|
+
const countResult = await this.#db.executeQuery(countQuery.build());
|
|
1482
1640
|
const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
|
|
1483
1641
|
if (total === 0) {
|
|
1484
1642
|
return {
|
|
@@ -1505,7 +1663,7 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1505
1663
|
}
|
|
1506
1664
|
selectQuery.limit(limitValue).offset(start);
|
|
1507
1665
|
const { sql, params } = selectQuery.build();
|
|
1508
|
-
const results = await this.
|
|
1666
|
+
const results = await this.#db.executeQuery({ sql, params });
|
|
1509
1667
|
const scores = Array.isArray(results) ? results.map(transformScoreRow) : [];
|
|
1510
1668
|
return {
|
|
1511
1669
|
pagination: {
|
|
@@ -1519,7 +1677,7 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1519
1677
|
} catch (error) {
|
|
1520
1678
|
throw new MastraError(
|
|
1521
1679
|
{
|
|
1522
|
-
id: "
|
|
1680
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_SCORER_ID", "FAILED"),
|
|
1523
1681
|
domain: ErrorDomain.STORAGE,
|
|
1524
1682
|
category: ErrorCategory.THIRD_PARTY
|
|
1525
1683
|
},
|
|
@@ -1535,9 +1693,9 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1535
1693
|
const { page, perPage: perPageInput } = pagination;
|
|
1536
1694
|
const perPage = normalizePerPage(perPageInput, 100);
|
|
1537
1695
|
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1538
|
-
const fullTableName = this.
|
|
1696
|
+
const fullTableName = this.#db.getTableName(TABLE_SCORERS);
|
|
1539
1697
|
const countQuery = createSqlBuilder().count().from(fullTableName).where("runId = ?", runId);
|
|
1540
|
-
const countResult = await this.
|
|
1698
|
+
const countResult = await this.#db.executeQuery(countQuery.build());
|
|
1541
1699
|
const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
|
|
1542
1700
|
if (total === 0) {
|
|
1543
1701
|
return {
|
|
@@ -1554,7 +1712,7 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1554
1712
|
const limitValue = perPageInput === false ? total : perPage;
|
|
1555
1713
|
const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("runId = ?", runId).limit(limitValue).offset(start);
|
|
1556
1714
|
const { sql, params } = selectQuery.build();
|
|
1557
|
-
const results = await this.
|
|
1715
|
+
const results = await this.#db.executeQuery({ sql, params });
|
|
1558
1716
|
const scores = Array.isArray(results) ? results.map(transformScoreRow) : [];
|
|
1559
1717
|
return {
|
|
1560
1718
|
pagination: {
|
|
@@ -1568,7 +1726,7 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1568
1726
|
} catch (error) {
|
|
1569
1727
|
throw new MastraError(
|
|
1570
1728
|
{
|
|
1571
|
-
id: "
|
|
1729
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_RUN_ID", "FAILED"),
|
|
1572
1730
|
domain: ErrorDomain.STORAGE,
|
|
1573
1731
|
category: ErrorCategory.THIRD_PARTY
|
|
1574
1732
|
},
|
|
@@ -1585,9 +1743,9 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1585
1743
|
const { page, perPage: perPageInput } = pagination;
|
|
1586
1744
|
const perPage = normalizePerPage(perPageInput, 100);
|
|
1587
1745
|
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1588
|
-
const fullTableName = this.
|
|
1746
|
+
const fullTableName = this.#db.getTableName(TABLE_SCORERS);
|
|
1589
1747
|
const countQuery = createSqlBuilder().count().from(fullTableName).where("entityId = ?", entityId).andWhere("entityType = ?", entityType);
|
|
1590
|
-
const countResult = await this.
|
|
1748
|
+
const countResult = await this.#db.executeQuery(countQuery.build());
|
|
1591
1749
|
const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
|
|
1592
1750
|
if (total === 0) {
|
|
1593
1751
|
return {
|
|
@@ -1604,7 +1762,7 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1604
1762
|
const limitValue = perPageInput === false ? total : perPage;
|
|
1605
1763
|
const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("entityId = ?", entityId).andWhere("entityType = ?", entityType).limit(limitValue).offset(start);
|
|
1606
1764
|
const { sql, params } = selectQuery.build();
|
|
1607
|
-
const results = await this.
|
|
1765
|
+
const results = await this.#db.executeQuery({ sql, params });
|
|
1608
1766
|
const scores = Array.isArray(results) ? results.map(transformScoreRow) : [];
|
|
1609
1767
|
return {
|
|
1610
1768
|
pagination: {
|
|
@@ -1618,7 +1776,7 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1618
1776
|
} catch (error) {
|
|
1619
1777
|
throw new MastraError(
|
|
1620
1778
|
{
|
|
1621
|
-
id: "
|
|
1779
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_ENTITY_ID", "FAILED"),
|
|
1622
1780
|
domain: ErrorDomain.STORAGE,
|
|
1623
1781
|
category: ErrorCategory.THIRD_PARTY
|
|
1624
1782
|
},
|
|
@@ -1635,9 +1793,9 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1635
1793
|
const { page, perPage: perPageInput } = pagination;
|
|
1636
1794
|
const perPage = normalizePerPage(perPageInput, 100);
|
|
1637
1795
|
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1638
|
-
const fullTableName = this.
|
|
1796
|
+
const fullTableName = this.#db.getTableName(TABLE_SCORERS);
|
|
1639
1797
|
const countQuery = createSqlBuilder().count().from(fullTableName).where("traceId = ?", traceId).andWhere("spanId = ?", spanId);
|
|
1640
|
-
const countResult = await this.
|
|
1798
|
+
const countResult = await this.#db.executeQuery(countQuery.build());
|
|
1641
1799
|
const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
|
|
1642
1800
|
if (total === 0) {
|
|
1643
1801
|
return {
|
|
@@ -1654,7 +1812,7 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1654
1812
|
const limitValue = perPageInput === false ? total : perPage;
|
|
1655
1813
|
const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("traceId = ?", traceId).andWhere("spanId = ?", spanId).orderBy("createdAt", "DESC").limit(limitValue).offset(start);
|
|
1656
1814
|
const { sql, params } = selectQuery.build();
|
|
1657
|
-
const results = await this.
|
|
1815
|
+
const results = await this.#db.executeQuery({ sql, params });
|
|
1658
1816
|
const scores = Array.isArray(results) ? results.map(transformScoreRow) : [];
|
|
1659
1817
|
return {
|
|
1660
1818
|
pagination: {
|
|
@@ -1668,7 +1826,7 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1668
1826
|
} catch (error) {
|
|
1669
1827
|
throw new MastraError(
|
|
1670
1828
|
{
|
|
1671
|
-
id: "
|
|
1829
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_SPAN", "FAILED"),
|
|
1672
1830
|
domain: ErrorDomain.STORAGE,
|
|
1673
1831
|
category: ErrorCategory.THIRD_PARTY
|
|
1674
1832
|
},
|
|
@@ -1678,10 +1836,16 @@ var ScoresStorageD1 = class extends ScoresStorage {
|
|
|
1678
1836
|
}
|
|
1679
1837
|
};
|
|
1680
1838
|
var WorkflowsStorageD1 = class extends WorkflowsStorage {
|
|
1681
|
-
|
|
1682
|
-
constructor(
|
|
1839
|
+
#db;
|
|
1840
|
+
constructor(config) {
|
|
1683
1841
|
super();
|
|
1684
|
-
this
|
|
1842
|
+
this.#db = new D1DB(resolveD1Config(config));
|
|
1843
|
+
}
|
|
1844
|
+
async init() {
|
|
1845
|
+
await this.#db.createTable({ tableName: TABLE_WORKFLOW_SNAPSHOT, schema: TABLE_SCHEMAS[TABLE_WORKFLOW_SNAPSHOT] });
|
|
1846
|
+
}
|
|
1847
|
+
async dangerouslyClearAll() {
|
|
1848
|
+
await this.#db.clearTable({ tableName: TABLE_WORKFLOW_SNAPSHOT });
|
|
1685
1849
|
}
|
|
1686
1850
|
updateWorkflowResults({
|
|
1687
1851
|
// workflowName,
|
|
@@ -1703,11 +1867,13 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
|
|
|
1703
1867
|
workflowName,
|
|
1704
1868
|
runId,
|
|
1705
1869
|
resourceId,
|
|
1706
|
-
snapshot
|
|
1870
|
+
snapshot,
|
|
1871
|
+
createdAt,
|
|
1872
|
+
updatedAt
|
|
1707
1873
|
}) {
|
|
1708
|
-
const fullTableName = this.
|
|
1874
|
+
const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
|
|
1709
1875
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1710
|
-
const currentSnapshot = await this.
|
|
1876
|
+
const currentSnapshot = await this.#db.load({
|
|
1711
1877
|
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
1712
1878
|
keys: { workflow_name: workflowName, run_id: runId }
|
|
1713
1879
|
});
|
|
@@ -1715,16 +1881,16 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
|
|
|
1715
1881
|
...currentSnapshot,
|
|
1716
1882
|
resourceId,
|
|
1717
1883
|
snapshot: JSON.stringify(snapshot),
|
|
1718
|
-
updatedAt: now
|
|
1884
|
+
updatedAt: updatedAt ? updatedAt.toISOString() : now
|
|
1719
1885
|
} : {
|
|
1720
1886
|
workflow_name: workflowName,
|
|
1721
1887
|
run_id: runId,
|
|
1722
1888
|
resourceId,
|
|
1723
1889
|
snapshot,
|
|
1724
|
-
createdAt: now,
|
|
1725
|
-
updatedAt: now
|
|
1890
|
+
createdAt: createdAt ? createdAt.toISOString() : now,
|
|
1891
|
+
updatedAt: updatedAt ? updatedAt.toISOString() : now
|
|
1726
1892
|
};
|
|
1727
|
-
const processedRecord = await this.
|
|
1893
|
+
const processedRecord = await this.#db.processRecord(persisting);
|
|
1728
1894
|
const columns = Object.keys(processedRecord);
|
|
1729
1895
|
const values = Object.values(processedRecord);
|
|
1730
1896
|
const updateMap = {
|
|
@@ -1735,11 +1901,11 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
|
|
|
1735
1901
|
const query = createSqlBuilder().insert(fullTableName, columns, values, ["workflow_name", "run_id"], updateMap);
|
|
1736
1902
|
const { sql, params } = query.build();
|
|
1737
1903
|
try {
|
|
1738
|
-
await this.
|
|
1904
|
+
await this.#db.executeQuery({ sql, params });
|
|
1739
1905
|
} catch (error) {
|
|
1740
1906
|
throw new MastraError(
|
|
1741
1907
|
{
|
|
1742
|
-
id: "
|
|
1908
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
|
|
1743
1909
|
domain: ErrorDomain.STORAGE,
|
|
1744
1910
|
category: ErrorCategory.THIRD_PARTY,
|
|
1745
1911
|
text: `Failed to persist workflow snapshot: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -1753,7 +1919,7 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
|
|
|
1753
1919
|
const { workflowName, runId } = params;
|
|
1754
1920
|
this.logger.debug("Loading workflow snapshot", { workflowName, runId });
|
|
1755
1921
|
try {
|
|
1756
|
-
const d = await this.
|
|
1922
|
+
const d = await this.#db.load({
|
|
1757
1923
|
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
1758
1924
|
keys: {
|
|
1759
1925
|
workflow_name: workflowName,
|
|
@@ -1764,7 +1930,7 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
|
|
|
1764
1930
|
} catch (error) {
|
|
1765
1931
|
throw new MastraError(
|
|
1766
1932
|
{
|
|
1767
|
-
id: "
|
|
1933
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
|
|
1768
1934
|
domain: ErrorDomain.STORAGE,
|
|
1769
1935
|
category: ErrorCategory.THIRD_PARTY,
|
|
1770
1936
|
text: `Failed to load workflow snapshot: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -1780,7 +1946,7 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
|
|
|
1780
1946
|
try {
|
|
1781
1947
|
parsedSnapshot = JSON.parse(row.snapshot);
|
|
1782
1948
|
} catch (e) {
|
|
1783
|
-
|
|
1949
|
+
this.logger.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
1784
1950
|
}
|
|
1785
1951
|
}
|
|
1786
1952
|
return {
|
|
@@ -1801,7 +1967,7 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
|
|
|
1801
1967
|
resourceId,
|
|
1802
1968
|
status
|
|
1803
1969
|
} = {}) {
|
|
1804
|
-
const fullTableName = this.
|
|
1970
|
+
const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
|
|
1805
1971
|
try {
|
|
1806
1972
|
const builder = createSqlBuilder().select().from(fullTableName);
|
|
1807
1973
|
const countBuilder = createSqlBuilder().count().from(fullTableName);
|
|
@@ -1811,12 +1977,12 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
|
|
|
1811
1977
|
countBuilder.whereAnd("json_extract(snapshot, '$.status') = ?", status);
|
|
1812
1978
|
}
|
|
1813
1979
|
if (resourceId) {
|
|
1814
|
-
const hasResourceId = await this.
|
|
1980
|
+
const hasResourceId = await this.#db.hasColumn(fullTableName, "resourceId");
|
|
1815
1981
|
if (hasResourceId) {
|
|
1816
1982
|
builder.whereAnd("resourceId = ?", resourceId);
|
|
1817
1983
|
countBuilder.whereAnd("resourceId = ?", resourceId);
|
|
1818
1984
|
} else {
|
|
1819
|
-
|
|
1985
|
+
this.logger.warn(`[${fullTableName}] resourceId column not found. Skipping resourceId filter.`);
|
|
1820
1986
|
}
|
|
1821
1987
|
}
|
|
1822
1988
|
if (fromDate) {
|
|
@@ -1837,20 +2003,20 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
|
|
|
1837
2003
|
let total = 0;
|
|
1838
2004
|
if (perPage !== void 0 && page !== void 0) {
|
|
1839
2005
|
const { sql: countSql, params: countParams } = countBuilder.build();
|
|
1840
|
-
const countResult = await this.
|
|
2006
|
+
const countResult = await this.#db.executeQuery({
|
|
1841
2007
|
sql: countSql,
|
|
1842
2008
|
params: countParams,
|
|
1843
2009
|
first: true
|
|
1844
2010
|
});
|
|
1845
2011
|
total = Number(countResult?.count ?? 0);
|
|
1846
2012
|
}
|
|
1847
|
-
const results = await this.
|
|
2013
|
+
const results = await this.#db.executeQuery({ sql, params });
|
|
1848
2014
|
const runs = (isArrayOfRecords(results) ? results : []).map((row) => this.parseWorkflowRun(row));
|
|
1849
2015
|
return { runs, total: total || runs.length };
|
|
1850
2016
|
} catch (error) {
|
|
1851
2017
|
throw new MastraError(
|
|
1852
2018
|
{
|
|
1853
|
-
id: "
|
|
2019
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_WORKFLOW_RUNS", "FAILED"),
|
|
1854
2020
|
domain: ErrorDomain.STORAGE,
|
|
1855
2021
|
category: ErrorCategory.THIRD_PARTY,
|
|
1856
2022
|
text: `Failed to retrieve workflow runs: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -1867,7 +2033,7 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
|
|
|
1867
2033
|
runId,
|
|
1868
2034
|
workflowName
|
|
1869
2035
|
}) {
|
|
1870
|
-
const fullTableName = this.
|
|
2036
|
+
const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
|
|
1871
2037
|
try {
|
|
1872
2038
|
const conditions = [];
|
|
1873
2039
|
const params = [];
|
|
@@ -1881,13 +2047,13 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
|
|
|
1881
2047
|
}
|
|
1882
2048
|
const whereClause = conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : "";
|
|
1883
2049
|
const sql = `SELECT * FROM ${fullTableName} ${whereClause} ORDER BY createdAt DESC LIMIT 1`;
|
|
1884
|
-
const result = await this.
|
|
2050
|
+
const result = await this.#db.executeQuery({ sql, params, first: true });
|
|
1885
2051
|
if (!result) return null;
|
|
1886
2052
|
return this.parseWorkflowRun(result);
|
|
1887
2053
|
} catch (error) {
|
|
1888
2054
|
throw new MastraError(
|
|
1889
2055
|
{
|
|
1890
|
-
id: "
|
|
2056
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
|
|
1891
2057
|
domain: ErrorDomain.STORAGE,
|
|
1892
2058
|
category: ErrorCategory.THIRD_PARTY,
|
|
1893
2059
|
text: `Failed to retrieve workflow run by ID: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -1897,13 +2063,31 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
|
|
|
1897
2063
|
);
|
|
1898
2064
|
}
|
|
1899
2065
|
}
|
|
2066
|
+
async deleteWorkflowRunById({ runId, workflowName }) {
|
|
2067
|
+
const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
|
|
2068
|
+
try {
|
|
2069
|
+
const sql = `DELETE FROM ${fullTableName} WHERE workflow_name = ? AND run_id = ?`;
|
|
2070
|
+
const params = [workflowName, runId];
|
|
2071
|
+
await this.#db.executeQuery({ sql, params });
|
|
2072
|
+
} catch (error) {
|
|
2073
|
+
throw new MastraError(
|
|
2074
|
+
{
|
|
2075
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
|
|
2076
|
+
domain: ErrorDomain.STORAGE,
|
|
2077
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2078
|
+
text: `Failed to delete workflow run by ID: ${error instanceof Error ? error.message : String(error)}`,
|
|
2079
|
+
details: { runId, workflowName }
|
|
2080
|
+
},
|
|
2081
|
+
error
|
|
2082
|
+
);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
1900
2085
|
};
|
|
1901
2086
|
|
|
1902
2087
|
// src/storage/index.ts
|
|
1903
2088
|
var D1Store = class extends MastraStorage {
|
|
1904
2089
|
client;
|
|
1905
2090
|
binding;
|
|
1906
|
-
// D1Database binding
|
|
1907
2091
|
tablePrefix;
|
|
1908
2092
|
stores;
|
|
1909
2093
|
/**
|
|
@@ -1912,7 +2096,7 @@ var D1Store = class extends MastraStorage {
|
|
|
1912
2096
|
*/
|
|
1913
2097
|
constructor(config) {
|
|
1914
2098
|
try {
|
|
1915
|
-
super({ id: config.id, name: "D1" });
|
|
2099
|
+
super({ id: config.id, name: "D1", disableInit: config.disableInit });
|
|
1916
2100
|
if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) {
|
|
1917
2101
|
throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
|
|
1918
2102
|
}
|
|
@@ -1950,7 +2134,7 @@ var D1Store = class extends MastraStorage {
|
|
|
1950
2134
|
} catch (error) {
|
|
1951
2135
|
throw new MastraError(
|
|
1952
2136
|
{
|
|
1953
|
-
id: "
|
|
2137
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "INITIALIZATION", "FAILED"),
|
|
1954
2138
|
domain: ErrorDomain.STORAGE,
|
|
1955
2139
|
category: ErrorCategory.SYSTEM,
|
|
1956
2140
|
text: "Error initializing D1Store"
|
|
@@ -1958,189 +2142,26 @@ var D1Store = class extends MastraStorage {
|
|
|
1958
2142
|
error
|
|
1959
2143
|
);
|
|
1960
2144
|
}
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
}
|
|
2145
|
+
let scores;
|
|
2146
|
+
let workflows;
|
|
2147
|
+
let memory;
|
|
2148
|
+
if (this.binding) {
|
|
2149
|
+
const domainConfig = { binding: this.binding, tablePrefix: this.tablePrefix };
|
|
2150
|
+
scores = new ScoresStorageD1(domainConfig);
|
|
2151
|
+
workflows = new WorkflowsStorageD1(domainConfig);
|
|
2152
|
+
memory = new MemoryStorageD1(domainConfig);
|
|
2153
|
+
} else {
|
|
2154
|
+
const domainConfig = { client: this.client, tablePrefix: this.tablePrefix };
|
|
2155
|
+
scores = new ScoresStorageD1(domainConfig);
|
|
2156
|
+
workflows = new WorkflowsStorageD1(domainConfig);
|
|
2157
|
+
memory = new MemoryStorageD1(domainConfig);
|
|
2158
|
+
}
|
|
1975
2159
|
this.stores = {
|
|
1976
|
-
operations,
|
|
1977
2160
|
scores,
|
|
1978
2161
|
workflows,
|
|
1979
2162
|
memory
|
|
1980
2163
|
};
|
|
1981
2164
|
}
|
|
1982
|
-
get supports() {
|
|
1983
|
-
return {
|
|
1984
|
-
selectByIncludeResourceScope: true,
|
|
1985
|
-
resourceWorkingMemory: true,
|
|
1986
|
-
hasColumn: true,
|
|
1987
|
-
createTable: true,
|
|
1988
|
-
deleteMessages: false,
|
|
1989
|
-
listScoresBySpan: true
|
|
1990
|
-
};
|
|
1991
|
-
}
|
|
1992
|
-
async createTable({
|
|
1993
|
-
tableName,
|
|
1994
|
-
schema
|
|
1995
|
-
}) {
|
|
1996
|
-
return this.stores.operations.createTable({ tableName, schema });
|
|
1997
|
-
}
|
|
1998
|
-
/**
|
|
1999
|
-
* Alters table schema to add columns if they don't exist
|
|
2000
|
-
* @param tableName Name of the table
|
|
2001
|
-
* @param schema Schema of the table
|
|
2002
|
-
* @param ifNotExists Array of column names to add if they don't exist
|
|
2003
|
-
*/
|
|
2004
|
-
async alterTable({
|
|
2005
|
-
tableName,
|
|
2006
|
-
schema,
|
|
2007
|
-
ifNotExists
|
|
2008
|
-
}) {
|
|
2009
|
-
return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
|
|
2010
|
-
}
|
|
2011
|
-
async clearTable({ tableName }) {
|
|
2012
|
-
return this.stores.operations.clearTable({ tableName });
|
|
2013
|
-
}
|
|
2014
|
-
async dropTable({ tableName }) {
|
|
2015
|
-
return this.stores.operations.dropTable({ tableName });
|
|
2016
|
-
}
|
|
2017
|
-
async hasColumn(table, column) {
|
|
2018
|
-
return this.stores.operations.hasColumn(table, column);
|
|
2019
|
-
}
|
|
2020
|
-
async insert({ tableName, record }) {
|
|
2021
|
-
return this.stores.operations.insert({ tableName, record });
|
|
2022
|
-
}
|
|
2023
|
-
async load({ tableName, keys }) {
|
|
2024
|
-
return this.stores.operations.load({ tableName, keys });
|
|
2025
|
-
}
|
|
2026
|
-
async getThreadById({ threadId }) {
|
|
2027
|
-
return this.stores.memory.getThreadById({ threadId });
|
|
2028
|
-
}
|
|
2029
|
-
async saveThread({ thread }) {
|
|
2030
|
-
return this.stores.memory.saveThread({ thread });
|
|
2031
|
-
}
|
|
2032
|
-
async updateThread({
|
|
2033
|
-
id,
|
|
2034
|
-
title,
|
|
2035
|
-
metadata
|
|
2036
|
-
}) {
|
|
2037
|
-
return this.stores.memory.updateThread({ id, title, metadata });
|
|
2038
|
-
}
|
|
2039
|
-
async deleteThread({ threadId }) {
|
|
2040
|
-
return this.stores.memory.deleteThread({ threadId });
|
|
2041
|
-
}
|
|
2042
|
-
async saveMessages(args) {
|
|
2043
|
-
return this.stores.memory.saveMessages(args);
|
|
2044
|
-
}
|
|
2045
|
-
async updateWorkflowResults({
|
|
2046
|
-
workflowName,
|
|
2047
|
-
runId,
|
|
2048
|
-
stepId,
|
|
2049
|
-
result,
|
|
2050
|
-
requestContext
|
|
2051
|
-
}) {
|
|
2052
|
-
return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
|
|
2053
|
-
}
|
|
2054
|
-
async updateWorkflowState({
|
|
2055
|
-
workflowName,
|
|
2056
|
-
runId,
|
|
2057
|
-
opts
|
|
2058
|
-
}) {
|
|
2059
|
-
return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
|
|
2060
|
-
}
|
|
2061
|
-
async persistWorkflowSnapshot({
|
|
2062
|
-
workflowName,
|
|
2063
|
-
runId,
|
|
2064
|
-
resourceId,
|
|
2065
|
-
snapshot
|
|
2066
|
-
}) {
|
|
2067
|
-
return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
|
|
2068
|
-
}
|
|
2069
|
-
async loadWorkflowSnapshot(params) {
|
|
2070
|
-
return this.stores.workflows.loadWorkflowSnapshot(params);
|
|
2071
|
-
}
|
|
2072
|
-
async listWorkflowRuns(args = {}) {
|
|
2073
|
-
return this.stores.workflows.listWorkflowRuns(args);
|
|
2074
|
-
}
|
|
2075
|
-
async getWorkflowRunById({
|
|
2076
|
-
runId,
|
|
2077
|
-
workflowName
|
|
2078
|
-
}) {
|
|
2079
|
-
return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
|
|
2080
|
-
}
|
|
2081
|
-
/**
|
|
2082
|
-
* Insert multiple records in a batch operation
|
|
2083
|
-
* @param tableName The table to insert into
|
|
2084
|
-
* @param records The records to insert
|
|
2085
|
-
*/
|
|
2086
|
-
async batchInsert({ tableName, records }) {
|
|
2087
|
-
return this.stores.operations.batchInsert({ tableName, records });
|
|
2088
|
-
}
|
|
2089
|
-
async updateMessages(_args) {
|
|
2090
|
-
return this.stores.memory.updateMessages(_args);
|
|
2091
|
-
}
|
|
2092
|
-
async getResourceById({ resourceId }) {
|
|
2093
|
-
return this.stores.memory.getResourceById({ resourceId });
|
|
2094
|
-
}
|
|
2095
|
-
async saveResource({ resource }) {
|
|
2096
|
-
return this.stores.memory.saveResource({ resource });
|
|
2097
|
-
}
|
|
2098
|
-
async updateResource({
|
|
2099
|
-
resourceId,
|
|
2100
|
-
workingMemory,
|
|
2101
|
-
metadata
|
|
2102
|
-
}) {
|
|
2103
|
-
return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
|
|
2104
|
-
}
|
|
2105
|
-
async getScoreById({ id: _id }) {
|
|
2106
|
-
return this.stores.scores.getScoreById({ id: _id });
|
|
2107
|
-
}
|
|
2108
|
-
async saveScore(_score) {
|
|
2109
|
-
return this.stores.scores.saveScore(_score);
|
|
2110
|
-
}
|
|
2111
|
-
async listScoresByRunId({
|
|
2112
|
-
runId: _runId,
|
|
2113
|
-
pagination: _pagination
|
|
2114
|
-
}) {
|
|
2115
|
-
return this.stores.scores.listScoresByRunId({ runId: _runId, pagination: _pagination });
|
|
2116
|
-
}
|
|
2117
|
-
async listScoresByEntityId({
|
|
2118
|
-
entityId: _entityId,
|
|
2119
|
-
entityType: _entityType,
|
|
2120
|
-
pagination: _pagination
|
|
2121
|
-
}) {
|
|
2122
|
-
return this.stores.scores.listScoresByEntityId({
|
|
2123
|
-
entityId: _entityId,
|
|
2124
|
-
entityType: _entityType,
|
|
2125
|
-
pagination: _pagination
|
|
2126
|
-
});
|
|
2127
|
-
}
|
|
2128
|
-
async listScoresByScorerId({
|
|
2129
|
-
scorerId,
|
|
2130
|
-
pagination,
|
|
2131
|
-
entityId,
|
|
2132
|
-
entityType,
|
|
2133
|
-
source
|
|
2134
|
-
}) {
|
|
2135
|
-
return this.stores.scores.listScoresByScorerId({ scorerId, pagination, entityId, entityType, source });
|
|
2136
|
-
}
|
|
2137
|
-
async listScoresBySpan({
|
|
2138
|
-
traceId,
|
|
2139
|
-
spanId,
|
|
2140
|
-
pagination
|
|
2141
|
-
}) {
|
|
2142
|
-
return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination });
|
|
2143
|
-
}
|
|
2144
2165
|
/**
|
|
2145
2166
|
* Close the database connection
|
|
2146
2167
|
* No explicit cleanup needed for D1 in either REST or Workers Binding mode
|
|
@@ -2150,6 +2171,6 @@ var D1Store = class extends MastraStorage {
|
|
|
2150
2171
|
}
|
|
2151
2172
|
};
|
|
2152
2173
|
|
|
2153
|
-
export { D1Store };
|
|
2174
|
+
export { D1Store, MemoryStorageD1, ScoresStorageD1, WorkflowsStorageD1 };
|
|
2154
2175
|
//# sourceMappingURL=index.js.map
|
|
2155
2176
|
//# sourceMappingURL=index.js.map
|