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