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