@mastra/pg 0.10.2-alpha.0 → 0.10.2-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1000,18 +1000,23 @@ var PostgresStore = class extends storage.MastraStorage {
1000
1000
  throw error;
1001
1001
  }
1002
1002
  }
1003
+ /**
1004
+ * @deprecated use getTracesPaginated instead
1005
+ */
1003
1006
  async getTraces(args) {
1004
- const {
1005
- name,
1006
- scope,
1007
- page,
1008
- perPage: perPageInput,
1009
- attributes,
1010
- filters,
1011
- fromDate,
1012
- toDate,
1013
- returnPaginationResults
1014
- } = args;
1007
+ if (args.fromDate || args.toDate) {
1008
+ args.dateRange = {
1009
+ start: args.fromDate,
1010
+ end: args.toDate
1011
+ };
1012
+ }
1013
+ const result = await this.getTracesPaginated(args);
1014
+ return result.traces;
1015
+ }
1016
+ async getTracesPaginated(args) {
1017
+ const { name, scope, page = 0, perPage: perPageInput, attributes, filters, dateRange } = args;
1018
+ const fromDate = dateRange?.start;
1019
+ const toDate = dateRange?.end;
1015
1020
  const perPage = perPageInput !== void 0 ? perPageInput : 100;
1016
1021
  const currentOffset = page * perPage;
1017
1022
  const queryParams = [];
@@ -1051,7 +1056,7 @@ var PostgresStore = class extends storage.MastraStorage {
1051
1056
  const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(storage.TABLE_TRACES)} ${whereClause}`;
1052
1057
  const countResult = await this.db.one(countQuery, queryParams);
1053
1058
  const total = parseInt(countResult.count, 10);
1054
- if (total === 0 && returnPaginationResults) {
1059
+ if (total === 0) {
1055
1060
  return {
1056
1061
  traces: [],
1057
1062
  total: 0,
@@ -1059,10 +1064,10 @@ var PostgresStore = class extends storage.MastraStorage {
1059
1064
  perPage,
1060
1065
  hasMore: false
1061
1066
  };
1062
- } else if (total === 0) {
1063
- return [];
1064
1067
  }
1065
- const dataQuery = `SELECT * FROM ${this.getTableName(storage.TABLE_TRACES)} ${whereClause} ORDER BY "createdAt" DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
1068
+ const dataQuery = `SELECT * FROM ${this.getTableName(
1069
+ storage.TABLE_TRACES
1070
+ )} ${whereClause} ORDER BY "createdAt" DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
1066
1071
  const finalQueryParams = [...queryParams, perPage, currentOffset];
1067
1072
  const rows = await this.db.manyOrNone(dataQuery, finalQueryParams);
1068
1073
  const traces = rows.map((row) => ({
@@ -1081,17 +1086,13 @@ var PostgresStore = class extends storage.MastraStorage {
1081
1086
  other: row.other,
1082
1087
  createdAt: row.createdAt
1083
1088
  }));
1084
- if (returnPaginationResults) {
1085
- return {
1086
- traces,
1087
- total,
1088
- page,
1089
- perPage,
1090
- hasMore: currentOffset + traces.length < total
1091
- };
1092
- } else {
1093
- return traces;
1094
- }
1089
+ return {
1090
+ traces,
1091
+ total,
1092
+ page,
1093
+ perPage,
1094
+ hasMore: currentOffset + traces.length < total
1095
+ };
1095
1096
  }
1096
1097
  async setupSchema() {
1097
1098
  if (!this.schema || this.schemaSetupComplete) {
@@ -1170,6 +1171,48 @@ var PostgresStore = class extends storage.MastraStorage {
1170
1171
  throw error;
1171
1172
  }
1172
1173
  }
1174
+ getDefaultValue(type) {
1175
+ switch (type) {
1176
+ case "timestamp":
1177
+ return "DEFAULT NOW()";
1178
+ case "jsonb":
1179
+ return "DEFAULT '{}'::jsonb";
1180
+ default:
1181
+ return super.getDefaultValue(type);
1182
+ }
1183
+ }
1184
+ /**
1185
+ * Alters table schema to add columns if they don't exist
1186
+ * @param tableName Name of the table
1187
+ * @param schema Schema of the table
1188
+ * @param ifNotExists Array of column names to add if they don't exist
1189
+ */
1190
+ async alterTable({
1191
+ tableName,
1192
+ schema,
1193
+ ifNotExists
1194
+ }) {
1195
+ const fullTableName = this.getTableName(tableName);
1196
+ try {
1197
+ for (const columnName of ifNotExists) {
1198
+ if (schema[columnName]) {
1199
+ const columnDef = schema[columnName];
1200
+ const sqlType = this.getSqlType(columnDef.type);
1201
+ const nullable = columnDef.nullable === false ? "NOT NULL" : "";
1202
+ const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
1203
+ const parsedColumnName = utils.parseSqlIdentifier(columnName, "column name");
1204
+ const alterSql = `ALTER TABLE ${fullTableName} ADD COLUMN IF NOT EXISTS "${parsedColumnName}" ${sqlType} ${nullable} ${defaultValue}`.trim();
1205
+ await this.db.none(alterSql);
1206
+ this.logger?.debug?.(`Ensured column ${parsedColumnName} exists in table ${fullTableName}`);
1207
+ }
1208
+ }
1209
+ } catch (error) {
1210
+ this.logger?.error?.(
1211
+ `Error altering table ${tableName}: ${error instanceof Error ? error.message : String(error)}`
1212
+ );
1213
+ throw new Error(`Failed to alter table ${tableName}: ${error}`);
1214
+ }
1215
+ }
1173
1216
  async clearTable({ tableName }) {
1174
1217
  try {
1175
1218
  await this.db.none(`TRUNCATE TABLE ${this.getTableName(tableName)} CASCADE`);
@@ -1245,58 +1288,65 @@ var PostgresStore = class extends storage.MastraStorage {
1245
1288
  throw error;
1246
1289
  }
1247
1290
  }
1291
+ /**
1292
+ * @deprecated use getThreadsByResourceIdPaginated instead
1293
+ */
1248
1294
  async getThreadsByResourceId(args) {
1249
- const { resourceId, page, perPage: perPageInput } = args;
1295
+ const { resourceId } = args;
1250
1296
  try {
1251
1297
  const baseQuery = `FROM ${this.getTableName(storage.TABLE_THREADS)} WHERE "resourceId" = $1`;
1252
1298
  const queryParams = [resourceId];
1253
- if (page !== void 0) {
1254
- const perPage = perPageInput !== void 0 ? perPageInput : 100;
1255
- const currentOffset = page * perPage;
1256
- const countQuery = `SELECT COUNT(*) ${baseQuery}`;
1257
- const countResult = await this.db.one(countQuery, queryParams);
1258
- const total = parseInt(countResult.count, 10);
1259
- if (total === 0) {
1260
- return {
1261
- threads: [],
1262
- total: 0,
1263
- page,
1264
- perPage,
1265
- hasMore: false
1266
- };
1267
- }
1268
- const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "updatedAt" ${baseQuery} ORDER BY "createdAt" DESC LIMIT $2 OFFSET $3`;
1269
- const rows = await this.db.manyOrNone(dataQuery, [...queryParams, perPage, currentOffset]);
1270
- const threads = (rows || []).map((thread) => ({
1271
- ...thread,
1272
- metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
1273
- createdAt: thread.createdAt,
1274
- // Assuming already Date objects or ISO strings
1275
- updatedAt: thread.updatedAt
1276
- }));
1299
+ const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "updatedAt" ${baseQuery} ORDER BY "createdAt" DESC`;
1300
+ const rows = await this.db.manyOrNone(dataQuery, queryParams);
1301
+ return (rows || []).map((thread) => ({
1302
+ ...thread,
1303
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
1304
+ createdAt: thread.createdAt,
1305
+ updatedAt: thread.updatedAt
1306
+ }));
1307
+ } catch (error) {
1308
+ this.logger.error(`Error getting threads for resource ${resourceId}:`, error);
1309
+ return [];
1310
+ }
1311
+ }
1312
+ async getThreadsByResourceIdPaginated(args) {
1313
+ const { resourceId, page = 0, perPage: perPageInput } = args;
1314
+ try {
1315
+ const baseQuery = `FROM ${this.getTableName(storage.TABLE_THREADS)} WHERE "resourceId" = $1`;
1316
+ const queryParams = [resourceId];
1317
+ const perPage = perPageInput !== void 0 ? perPageInput : 100;
1318
+ const currentOffset = page * perPage;
1319
+ const countQuery = `SELECT COUNT(*) ${baseQuery}`;
1320
+ const countResult = await this.db.one(countQuery, queryParams);
1321
+ const total = parseInt(countResult.count, 10);
1322
+ if (total === 0) {
1277
1323
  return {
1278
- threads,
1279
- total,
1324
+ threads: [],
1325
+ total: 0,
1280
1326
  page,
1281
1327
  perPage,
1282
- hasMore: currentOffset + threads.length < total
1328
+ hasMore: false
1283
1329
  };
1284
- } else {
1285
- const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "updatedAt" ${baseQuery} ORDER BY "createdAt" DESC`;
1286
- const rows = await this.db.manyOrNone(dataQuery, queryParams);
1287
- return (rows || []).map((thread) => ({
1288
- ...thread,
1289
- metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
1290
- createdAt: thread.createdAt,
1291
- updatedAt: thread.updatedAt
1292
- }));
1293
1330
  }
1331
+ const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "updatedAt" ${baseQuery} ORDER BY "createdAt" DESC LIMIT $2 OFFSET $3`;
1332
+ const rows = await this.db.manyOrNone(dataQuery, [...queryParams, perPage, currentOffset]);
1333
+ const threads = (rows || []).map((thread) => ({
1334
+ ...thread,
1335
+ metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
1336
+ createdAt: thread.createdAt,
1337
+ // Assuming already Date objects or ISO strings
1338
+ updatedAt: thread.updatedAt
1339
+ }));
1340
+ return {
1341
+ threads,
1342
+ total,
1343
+ page,
1344
+ perPage,
1345
+ hasMore: currentOffset + threads.length < total
1346
+ };
1294
1347
  } catch (error) {
1295
1348
  this.logger.error(`Error getting threads for resource ${resourceId}:`, error);
1296
- if (page !== void 0) {
1297
- return { threads: [], total: 0, page, perPage: perPageInput || 100, hasMore: false };
1298
- }
1299
- return [];
1349
+ return { threads: [], total: 0, page, perPage: perPageInput || 100, hasMore: false };
1300
1350
  }
1301
1351
  }
1302
1352
  async saveThread({ thread }) {
@@ -1377,68 +1427,15 @@ var PostgresStore = class extends storage.MastraStorage {
1377
1427
  }
1378
1428
  }
1379
1429
  async getMessages(args) {
1380
- const { threadId, format, page, perPage: perPageInput, fromDate, toDate, selectBy } = args;
1430
+ const { threadId, format, selectBy } = args;
1381
1431
  const selectStatement = `SELECT id, content, role, type, "createdAt", thread_id AS "threadId"`;
1382
1432
  const orderByStatement = `ORDER BY "createdAt" DESC`;
1383
1433
  try {
1384
- if (page !== void 0) {
1385
- const perPage = perPageInput !== void 0 ? perPageInput : 40;
1386
- const currentOffset = page * perPage;
1387
- const conditions = [`thread_id = $1`];
1388
- const queryParams = [threadId];
1389
- let paramIndex = 2;
1390
- if (fromDate) {
1391
- conditions.push(`"createdAt" >= $${paramIndex++}`);
1392
- queryParams.push(fromDate);
1393
- }
1394
- if (toDate) {
1395
- conditions.push(`"createdAt" <= $${paramIndex++}`);
1396
- queryParams.push(toDate);
1397
- }
1398
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1399
- const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(storage.TABLE_MESSAGES)} ${whereClause}`;
1400
- const countResult = await this.db.one(countQuery, queryParams);
1401
- const total = parseInt(countResult.count, 10);
1402
- if (total === 0) {
1403
- return {
1404
- messages: [],
1405
- total: 0,
1406
- page,
1407
- perPage,
1408
- hasMore: false
1409
- };
1410
- }
1411
- const dataQuery = `${selectStatement} FROM ${this.getTableName(storage.TABLE_MESSAGES)} ${whereClause} ${orderByStatement} LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
1412
- const rows = await this.db.manyOrNone(dataQuery, [...queryParams, perPage, currentOffset]);
1413
- const fetchedMessages = (rows || []).map((message) => {
1414
- if (typeof message.content === "string") {
1415
- try {
1416
- message.content = JSON.parse(message.content);
1417
- } catch {
1418
- }
1419
- }
1420
- if (message.type === "v2") delete message.type;
1421
- return message;
1422
- });
1423
- const messagesToReturn = format === "v2" ? fetchedMessages.map(
1424
- (m) => ({
1425
- ...m,
1426
- content: m.content || { format: 2, parts: [{ type: "text", text: "" }] }
1427
- })
1428
- ) : fetchedMessages;
1429
- return {
1430
- messages: messagesToReturn,
1431
- total,
1432
- page,
1433
- perPage,
1434
- hasMore: currentOffset + fetchedMessages.length < total
1435
- };
1436
- } else {
1437
- let rows = [];
1438
- const include = selectBy?.include || [];
1439
- if (include.length) {
1440
- rows = await this.db.manyOrNone(
1441
- `
1434
+ let rows = [];
1435
+ const include = selectBy?.include || [];
1436
+ if (include.length) {
1437
+ rows = await this.db.manyOrNone(
1438
+ `
1442
1439
  WITH ordered_messages AS (
1443
1440
  SELECT
1444
1441
  *,
@@ -1468,53 +1465,103 @@ var PostgresStore = class extends storage.MastraStorage {
1468
1465
  )
1469
1466
  ORDER BY m."createdAt" ASC
1470
1467
  `,
1471
- // Keep ASC for final sorting after fetching context
1472
- [
1473
- threadId,
1474
- include.map((i) => i.id),
1475
- Math.max(0, ...include.map((i) => i.withPreviousMessages || 0)),
1476
- // Ensure non-negative
1477
- Math.max(0, ...include.map((i) => i.withNextMessages || 0))
1478
- // Ensure non-negative
1479
- ]
1480
- );
1481
- } else {
1482
- const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
1483
- if (limit === 0 && selectBy?.last !== false) ; else {
1484
- let query = `${selectStatement} FROM ${this.getTableName(storage.TABLE_MESSAGES)} WHERE thread_id = $1 ${orderByStatement}`;
1485
- const queryParams = [threadId];
1486
- if (limit !== void 0 && selectBy?.last !== false) {
1487
- query += ` LIMIT $2`;
1488
- queryParams.push(limit);
1489
- }
1490
- rows = await this.db.manyOrNone(query, queryParams);
1468
+ // Keep ASC for final sorting after fetching context
1469
+ [
1470
+ threadId,
1471
+ include.map((i) => i.id),
1472
+ Math.max(0, ...include.map((i) => i.withPreviousMessages || 0)),
1473
+ // Ensure non-negative
1474
+ Math.max(0, ...include.map((i) => i.withNextMessages || 0))
1475
+ // Ensure non-negative
1476
+ ]
1477
+ );
1478
+ } else {
1479
+ const limit = typeof selectBy?.last === `number` ? selectBy.last : 40;
1480
+ if (limit === 0 && selectBy?.last !== false) ; else {
1481
+ let query = `${selectStatement} FROM ${this.getTableName(
1482
+ storage.TABLE_MESSAGES
1483
+ )} WHERE thread_id = $1 ${orderByStatement}`;
1484
+ const queryParams = [threadId];
1485
+ if (limit !== void 0 && selectBy?.last !== false) {
1486
+ query += ` LIMIT $2`;
1487
+ queryParams.push(limit);
1491
1488
  }
1489
+ rows = await this.db.manyOrNone(query, queryParams);
1492
1490
  }
1493
- const fetchedMessages = (rows || []).map((message) => {
1494
- if (typeof message.content === "string") {
1495
- try {
1496
- message.content = JSON.parse(message.content);
1497
- } catch {
1498
- }
1499
- }
1500
- if (message.type === "v2") delete message.type;
1501
- return message;
1502
- });
1503
- const sortedMessages = fetchedMessages.sort(
1504
- (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
1505
- );
1506
- return format === "v2" ? sortedMessages.map(
1507
- (m) => ({ ...m, content: m.content || { format: 2, parts: [{ type: "text", text: "" }] } })
1508
- ) : sortedMessages;
1509
1491
  }
1492
+ const fetchedMessages = (rows || []).map((message) => {
1493
+ if (typeof message.content === "string") {
1494
+ try {
1495
+ message.content = JSON.parse(message.content);
1496
+ } catch {
1497
+ }
1498
+ }
1499
+ if (message.type === "v2") delete message.type;
1500
+ return message;
1501
+ });
1502
+ const sortedMessages = fetchedMessages.sort(
1503
+ (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
1504
+ );
1505
+ return format === "v2" ? sortedMessages.map(
1506
+ (m) => ({ ...m, content: m.content || { format: 2, parts: [{ type: "text", text: "" }] } })
1507
+ ) : sortedMessages;
1510
1508
  } catch (error) {
1511
1509
  this.logger.error("Error getting messages:", error);
1512
- if (page !== void 0) {
1513
- return { messages: [], total: 0, page, perPage: perPageInput || 40, hasMore: false };
1514
- }
1515
1510
  return [];
1516
1511
  }
1517
1512
  }
1513
+ async getMessagesPaginated(args) {
1514
+ const { threadId, format, selectBy } = args;
1515
+ const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
1516
+ const fromDate = dateRange?.start;
1517
+ const toDate = dateRange?.end;
1518
+ const selectStatement = `SELECT id, content, role, type, "createdAt", thread_id AS "threadId"`;
1519
+ const orderByStatement = `ORDER BY "createdAt" DESC`;
1520
+ try {
1521
+ const perPage = perPageInput !== void 0 ? perPageInput : 40;
1522
+ const currentOffset = page * perPage;
1523
+ const conditions = [`thread_id = $1`];
1524
+ const queryParams = [threadId];
1525
+ let paramIndex = 2;
1526
+ if (fromDate) {
1527
+ conditions.push(`"createdAt" >= $${paramIndex++}`);
1528
+ queryParams.push(fromDate);
1529
+ }
1530
+ if (toDate) {
1531
+ conditions.push(`"createdAt" <= $${paramIndex++}`);
1532
+ queryParams.push(toDate);
1533
+ }
1534
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1535
+ const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(storage.TABLE_MESSAGES)} ${whereClause}`;
1536
+ const countResult = await this.db.one(countQuery, queryParams);
1537
+ const total = parseInt(countResult.count, 10);
1538
+ if (total === 0) {
1539
+ return {
1540
+ messages: [],
1541
+ total: 0,
1542
+ page,
1543
+ perPage,
1544
+ hasMore: false
1545
+ };
1546
+ }
1547
+ const dataQuery = `${selectStatement} FROM ${this.getTableName(
1548
+ storage.TABLE_MESSAGES
1549
+ )} ${whereClause} ${orderByStatement} LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
1550
+ const rows = await this.db.manyOrNone(dataQuery, [...queryParams, perPage, currentOffset]);
1551
+ const list = new agent.MessageList().add(rows || [], "memory");
1552
+ const messagesToReturn = format === `v2` ? list.get.all.v2() : list.get.all.v1();
1553
+ return {
1554
+ messages: messagesToReturn,
1555
+ total,
1556
+ page,
1557
+ perPage,
1558
+ hasMore: currentOffset + rows.length < total
1559
+ };
1560
+ } catch (error) {
1561
+ this.logger.error("Error getting messages:", error);
1562
+ return { messages: [], total: 0, page, perPage: perPageInput || 40, hasMore: false };
1563
+ }
1564
+ }
1518
1565
  async saveMessages({
1519
1566
  messages,
1520
1567
  format
@@ -1725,8 +1772,10 @@ var PostgresStore = class extends storage.MastraStorage {
1725
1772
  async close() {
1726
1773
  this.pgp.end();
1727
1774
  }
1728
- async getEvals(options) {
1729
- const { agentName, type, page, perPage, limit, offset, fromDate, toDate } = options || {};
1775
+ async getEvals(options = {}) {
1776
+ const { agentName, type, page = 0, perPage = 100, dateRange } = options;
1777
+ const fromDate = dateRange?.start;
1778
+ const toDate = dateRange?.end;
1730
1779
  const conditions = [];
1731
1780
  const queryParams = [];
1732
1781
  let paramIndex = 1;
@@ -1751,45 +1800,26 @@ var PostgresStore = class extends storage.MastraStorage {
1751
1800
  const countQuery = `SELECT COUNT(*) FROM ${this.getTableName(storage.TABLE_EVALS)} ${whereClause}`;
1752
1801
  const countResult = await this.db.one(countQuery, queryParams);
1753
1802
  const total = parseInt(countResult.count, 10);
1754
- let currentLimit;
1755
- let currentOffset;
1756
- let currentPage = page;
1757
- let currentPerPage = perPage;
1758
- let hasMore = false;
1759
- if (limit !== void 0 && offset !== void 0) {
1760
- currentLimit = limit;
1761
- currentOffset = offset;
1762
- currentPage = void 0;
1763
- currentPerPage = void 0;
1764
- hasMore = currentOffset + currentLimit < total;
1765
- } else if (page !== void 0 && perPage !== void 0) {
1766
- currentLimit = perPage;
1767
- currentOffset = page * perPage;
1768
- hasMore = currentOffset + currentLimit < total;
1769
- } else {
1770
- currentLimit = perPage || 100;
1771
- currentOffset = (page || 0) * currentLimit;
1772
- if (page === void 0) currentPage = 0;
1773
- if (currentPerPage === void 0) currentPerPage = currentLimit;
1774
- hasMore = currentOffset + currentLimit < total;
1775
- }
1803
+ const currentOffset = page * perPage;
1776
1804
  if (total === 0) {
1777
1805
  return {
1778
1806
  evals: [],
1779
1807
  total: 0,
1780
- page: currentPage,
1781
- perPage: currentPerPage,
1808
+ page,
1809
+ perPage,
1782
1810
  hasMore: false
1783
1811
  };
1784
1812
  }
1785
- const dataQuery = `SELECT * FROM ${this.getTableName(storage.TABLE_EVALS)} ${whereClause} ORDER BY created_at DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
1786
- const rows = await this.db.manyOrNone(dataQuery, [...queryParams, currentLimit, currentOffset]);
1813
+ const dataQuery = `SELECT * FROM ${this.getTableName(
1814
+ storage.TABLE_EVALS
1815
+ )} ${whereClause} ORDER BY created_at DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
1816
+ const rows = await this.db.manyOrNone(dataQuery, [...queryParams, perPage, currentOffset]);
1787
1817
  return {
1788
1818
  evals: rows?.map((row) => this.transformEvalRow(row)) ?? [],
1789
1819
  total,
1790
- page: currentPage,
1791
- perPage: currentPerPage,
1792
- hasMore
1820
+ page,
1821
+ perPage,
1822
+ hasMore: currentOffset + (rows?.length ?? 0) < total
1793
1823
  };
1794
1824
  }
1795
1825
  };