@mastra/pg 1.26.0-alpha.2 → 1.26.0-alpha.3

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.
@@ -3,7 +3,7 @@ name: mastra-pg
3
3
  description: Documentation for @mastra/pg. Use when working with @mastra/pg APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/pg"
6
- version: "1.26.0-alpha.2"
6
+ version: "1.26.0-alpha.3"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.26.0-alpha.2",
2
+ "version": "1.26.0-alpha.3",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -345,6 +345,29 @@ await store.upsert({
345
345
 
346
346
  For detailed setup instructions and best practices, see the [official Elasticsearch documentation](https://www.elastic.co/docs/solutions/search/get-started).
347
347
 
348
+ **Azure AI Search**:
349
+
350
+ ```ts
351
+ import { AzureAISearchVector } from '@mastra/azure-ai-search'
352
+
353
+ const store = new AzureAISearchVector({
354
+ id: 'azure-search-vectors',
355
+ endpoint: process.env.AZURE_AI_SEARCH_ENDPOINT!,
356
+ credential: process.env.AZURE_AI_SEARCH_CREDENTIAL!,
357
+ })
358
+
359
+ await store.createIndex({
360
+ indexName: 'my-collection',
361
+ dimension: 1536,
362
+ })
363
+
364
+ await store.upsert({
365
+ indexName: 'my-collection',
366
+ vectors: embeddings,
367
+ metadata: chunks.map(chunk => ({ text: chunk.text })),
368
+ })
369
+ ```
370
+
348
371
  **Couchbase**:
349
372
 
350
373
  ```ts
@@ -626,6 +649,17 @@ Index names must:
626
649
  - Example: `myindex-` isn't valid (ends with hyphen)
627
650
  - Example: `MyIndex` isn't valid (contains uppercase letters)
628
651
 
652
+ **Azure AI Search**:
653
+
654
+ Index names must:
655
+
656
+ - Use only lowercase letters, numbers, dashes (`-`), and underscore (`_`) characters
657
+ - Not start or end with a dash
658
+ - Be between 2 and 128 characters long
659
+ - Example: `my-index-123` and `my_index` are valid
660
+ - Example: `MyIndex` isn't valid (contains uppercase letters)
661
+ - Example: `my-index-` isn't valid (ends with a dash)
662
+
629
663
  ### Upserting Embeddings
630
664
 
631
665
  After creating an index, you can store embeddings along with their basic metadata:
package/dist/index.cjs CHANGED
@@ -10782,7 +10782,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
10782
10782
  hasMore: false
10783
10783
  };
10784
10784
  const limitValue = perPageInput === false ? total : perPage;
10785
- const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "createdAtZ", "updatedAt", "updatedAtZ" ${baseQuery} ORDER BY COALESCE("${field}Z", "${field}") ${direction} LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
10785
+ const dataQuery = `SELECT id, "resourceId", title, metadata, "createdAt", "createdAtZ", "updatedAt", "updatedAtZ" ${baseQuery} ORDER BY COALESCE("${field}Z", "${field}") ${direction}, "id" ${direction} LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
10786
10786
  return {
10787
10787
  threads: (await this.#db.readClient.manyOrNone(dataQuery, [
10788
10788
  ...queryParams,
@@ -10975,10 +10975,11 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
10975
10975
  return messages.sort((a, b) => {
10976
10976
  const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
10977
10977
  const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
10978
- if (aValue == null && bValue == null) return a.id.localeCompare(b.id);
10978
+ const idOrder = direction === "ASC" ? a.id.localeCompare(b.id) : b.id.localeCompare(a.id);
10979
+ if (aValue == null && bValue == null) return idOrder;
10979
10980
  if (aValue == null) return 1;
10980
10981
  if (bValue == null) return -1;
10981
- if (aValue === bValue) return a.id.localeCompare(b.id);
10982
+ if (aValue === bValue) return idOrder;
10982
10983
  if (typeof aValue === "number" && typeof bValue === "number") return direction === "ASC" ? aValue - bValue : bValue - aValue;
10983
10984
  return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
10984
10985
  });
@@ -11181,7 +11182,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11181
11182
  const metadataFilter = (0, _mastra_core_storage.validateStorageMetadataFilter)(filter?.metadata);
11182
11183
  try {
11183
11184
  const { field, direction } = this.parseOrderBy(orderBy, "ASC");
11184
- const orderByStatement = `ORDER BY "${field}" ${direction}`;
11185
+ const orderByStatement = `ORDER BY "${field}" ${direction}, "id" ${direction}`;
11185
11186
  const selectStatement = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"`;
11186
11187
  const tableName = getTableName$3({
11187
11188
  indexName: _mastra_core_storage.TABLE_MESSAGES,
@@ -11336,7 +11337,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11336
11337
  const metadataFilter = (0, _mastra_core_storage.validateStorageMetadataFilter)(filter?.metadata);
11337
11338
  try {
11338
11339
  const { field, direction } = this.parseOrderBy(orderBy, "ASC");
11339
- const orderByStatement = `ORDER BY "${field}" ${direction}`;
11340
+ const orderByStatement = `ORDER BY "${field}" ${direction}, "id" ${direction}`;
11340
11341
  const selectStatement = `SELECT id, content, role, type, "createdAt", "createdAtZ", thread_id AS "threadId", "resourceId"`;
11341
11342
  const tableName = getTableName$3({
11342
11343
  indexName: _mastra_core_storage.TABLE_MESSAGES,
@@ -14831,6 +14832,11 @@ function tableIndexes() {
14831
14832
  columns: "(\"tags\")",
14832
14833
  using: "gin"
14833
14834
  },
14835
+ {
14836
+ name: "mastra_score_events_scoreid_cursor_idx",
14837
+ table: TABLE_SCORE_EVENTS,
14838
+ columns: "(\"scoreId\", \"cursorId\" DESC)"
14839
+ },
14834
14840
  {
14835
14841
  name: "mastra_score_events_cursor_idx",
14836
14842
  table: TABLE_SCORE_EVENTS,
@@ -16963,13 +16969,32 @@ function pushScoreIdentity(acc, scorerId, scoreSource) {
16963
16969
  acc.params.push(scoreSource);
16964
16970
  }
16965
16971
  }
16972
+ function scoreRewriteConflict(row) {
16973
+ return `ON CONFLICT ("scoreId", "timestamp") DO UPDATE SET ${[
16974
+ ...Object.keys(row).filter((column) => column !== "scoreId" && column !== "timestamp").map((column) => `"${column}" = EXCLUDED."${column}"`),
16975
+ "\"cursorId\" = EXCLUDED.\"cursorId\"",
16976
+ "\"xactId\" = EXCLUDED.\"xactId\""
16977
+ ].join(", ")}`;
16978
+ }
16979
+ function collapseExactScoreConflicts(rows) {
16980
+ const records = /* @__PURE__ */ new Map();
16981
+ for (const row of rows) {
16982
+ const timestamp = new Date(row.timestamp).toISOString();
16983
+ const key = `${String(row.scoreId)}\u0000${timestamp}`;
16984
+ records.delete(key);
16985
+ records.set(key, row);
16986
+ }
16987
+ return [...records.values()];
16988
+ }
16966
16989
  async function createScore(client, schema, args) {
16967
- const insert = buildInsert(schema, TABLE_SCORE_EVENTS, [scoreRecordToRow(args.score)]);
16990
+ const row = scoreRecordToRow(args.score);
16991
+ const insert = buildInsert(schema, TABLE_SCORE_EVENTS, [row], scoreRewriteConflict(row));
16968
16992
  if (insert) await client.query(insert.text, insert.values);
16969
16993
  }
16970
16994
  async function batchCreateScores(client, schema, args) {
16971
16995
  if (args.scores.length === 0) return;
16972
- const insert = buildInsert(schema, TABLE_SCORE_EVENTS, args.scores.map(scoreRecordToRow));
16996
+ const rows = collapseExactScoreConflicts(args.scores.map(scoreRecordToRow));
16997
+ const insert = buildInsert(schema, TABLE_SCORE_EVENTS, rows, scoreRewriteConflict(rows[0]));
16973
16998
  if (insert) await client.query(insert.text, insert.values);
16974
16999
  }
16975
17000
  /**
@@ -16992,6 +17017,16 @@ async function deleteScores(client, schema, args) {
16992
17017
  }
16993
17018
  await client.query(`DELETE FROM ${table} WHERE ${conditions.join(" AND ")}`, values);
16994
17019
  }
17020
+ function latestScorePredicate(table, alias = "s") {
17021
+ return `NOT EXISTS (
17022
+ SELECT 1 FROM ${table} newer
17023
+ WHERE newer."scoreId" = ${alias}."scoreId"
17024
+ AND newer."cursorId" > ${alias}."cursorId"
17025
+ )`;
17026
+ }
17027
+ function applyLatestScorePredicate(acc, table) {
17028
+ acc.conditions.push(latestScorePredicate(table));
17029
+ }
16995
17030
  async function listScores(client, schema, args) {
16996
17031
  const { mode, filters, pagination, orderBy, after, limit } = _mastra_core_storage.listScoresArgsSchema.parse(args);
16997
17032
  const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
@@ -17002,28 +17037,50 @@ async function listScores(client, schema, args) {
17002
17037
  return listScoresPage(client, table, filters, pagination.page, pagination.perPage, orderBy.field, orderBy.direction);
17003
17038
  }
17004
17039
  async function getScoreById(client, schema, scoreId) {
17040
+ const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
17005
17041
  const row = await client.oneOrNone(`SELECT ${SCORE_SELECT_COLUMNS}
17006
- FROM ${qualifiedTable(schema, TABLE_SCORE_EVENTS)}
17042
+ FROM ${table}
17007
17043
  WHERE "scoreId" = $1
17008
- ORDER BY "timestamp" DESC
17044
+ ORDER BY "cursorId" DESC
17009
17045
  LIMIT 1`, [scoreId]);
17010
17046
  return row ? rowToScoreRecord(row) : null;
17011
17047
  }
17012
17048
  async function listScoresPage(client, table, filters, page, perPage, orderField, orderDir) {
17013
- return listSignalPage({
17049
+ const acc = newFilterAccumulator();
17050
+ applyScoreFilters(acc, filters);
17051
+ applyLatestScorePredicate(acc, table);
17052
+ const whereClause = whereOrEmpty(acc);
17053
+ const countRow = await client.oneOrNone(`SELECT COUNT(*)::text AS count FROM ${table} s ${whereClause}`, acc.params);
17054
+ const total = Number(countRow?.count ?? 0);
17055
+ let scores = [];
17056
+ if (total > 0) {
17057
+ const safeOrderField = (0, _mastra_core_utils.parseSqlIdentifier)(orderField, "order field");
17058
+ scores = (await client.manyOrNone(`SELECT ${SCORE_SELECT_COLUMNS}
17059
+ FROM ${table} s
17060
+ ${whereClause}
17061
+ ORDER BY "${safeOrderField}" ${orderDir}, "cursorId" ${orderDir}
17062
+ LIMIT $${acc.next++} OFFSET $${acc.next++}`, [
17063
+ ...acc.params,
17064
+ perPage,
17065
+ page * perPage
17066
+ ])).map(rowToScoreRecord);
17067
+ }
17068
+ const deltaCursor = deltaPollingFeatureEnabled() ? await readSignalStreamHeadCursor({
17014
17069
  client,
17015
17070
  table,
17016
17071
  filters,
17017
- page,
17018
- perPage,
17019
- orderField,
17020
- orderDir,
17021
- includeDeltaCursor: deltaPollingFeatureEnabled(),
17022
- selectColumns: SCORE_SELECT_COLUMNS,
17023
- responseKey: "scores",
17024
- applyFilters: applyScoreFilters,
17025
- mapRow: rowToScoreRecord
17026
- });
17072
+ applyFilters: applyScoreFilters
17073
+ }) : void 0;
17074
+ return {
17075
+ scores,
17076
+ pagination: {
17077
+ total,
17078
+ page,
17079
+ perPage,
17080
+ hasMore: (page + 1) * perPage < total
17081
+ },
17082
+ ...deltaCursor !== void 0 ? { deltaCursor } : {}
17083
+ };
17027
17084
  }
17028
17085
  async function listScoresDelta(client, table, filters, after, limit) {
17029
17086
  return listSignalDelta({
@@ -17039,12 +17096,14 @@ async function listScoresDelta(client, table, filters, after, limit) {
17039
17096
  });
17040
17097
  }
17041
17098
  async function runScoreAggregateQuery(client, schema, args, filters) {
17099
+ const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
17042
17100
  const acc = newFilterAccumulator();
17043
17101
  pushScoreIdentity(acc, args.scorerId, args.scoreSource);
17044
17102
  applyScoreFilters(acc, filters);
17103
+ applyLatestScorePredicate(acc, table);
17045
17104
  const sql = `
17046
17105
  SELECT ${aggregationSql(args.aggregation, "\"score\"")} AS "value"
17047
- FROM ${qualifiedTable(schema, TABLE_SCORE_EVENTS)}
17106
+ FROM ${table} s
17048
17107
  ${whereOrEmpty(acc)}
17049
17108
  `;
17050
17109
  const row = await client.oneOrNone(sql, acc.params);
@@ -17076,10 +17135,12 @@ async function getScoreBreakdown(client, schema, args) {
17076
17135
  });
17077
17136
  pushScoreIdentity(acc, args.scorerId, args.scoreSource);
17078
17137
  applyScoreFilters(acc, args.filters);
17138
+ const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
17139
+ applyLatestScorePredicate(acc, table);
17079
17140
  const sql = `
17080
17141
  SELECT ${resolved.map((e) => e.selectSql).join(", ")},
17081
17142
  ${aggregationSql(args.aggregation, "\"score\"")} AS "value"
17082
- FROM ${qualifiedTable(schema, TABLE_SCORE_EVENTS)}
17143
+ FROM ${table} s
17083
17144
  ${whereOrEmpty(acc)}
17084
17145
  GROUP BY ${resolved.map((e) => e.alias).join(", ")}
17085
17146
  ORDER BY "value" DESC NULLS LAST
@@ -17099,11 +17160,13 @@ async function getScoreTimeSeries(client, schema, args) {
17099
17160
  });
17100
17161
  pushScoreIdentity(acc, args.scorerId, args.scoreSource);
17101
17162
  applyScoreFilters(acc, args.filters);
17163
+ const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
17164
+ applyLatestScorePredicate(acc, table);
17102
17165
  const sql = `
17103
17166
  SELECT ${bucket} AS bucket,
17104
17167
  ${resolved.map((e) => e.selectSql).join(", ")},
17105
17168
  ${aggregationSql(args.aggregation, "\"score\"")} AS "value"
17106
- FROM ${qualifiedTable(schema, TABLE_SCORE_EVENTS)}
17169
+ FROM ${table} s
17107
17170
  ${whereOrEmpty(acc)}
17108
17171
  GROUP BY bucket, ${resolved.map((e) => e.alias).join(", ")}
17109
17172
  ORDER BY bucket
@@ -17121,10 +17184,12 @@ async function getScoreTimeSeries(client, schema, args) {
17121
17184
  const acc = newFilterAccumulator();
17122
17185
  pushScoreIdentity(acc, args.scorerId, args.scoreSource);
17123
17186
  applyScoreFilters(acc, args.filters);
17187
+ const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
17188
+ applyLatestScorePredicate(acc, table);
17124
17189
  const sql = `
17125
17190
  SELECT ${bucket} AS bucket,
17126
17191
  ${aggregationSql(args.aggregation, "\"score\"")} AS "value"
17127
- FROM ${qualifiedTable(schema, TABLE_SCORE_EVENTS)}
17192
+ FROM ${table} s
17128
17193
  ${whereOrEmpty(acc)}
17129
17194
  GROUP BY bucket
17130
17195
  ORDER BY bucket
@@ -17144,9 +17209,11 @@ async function getScorePercentiles(client, schema, args) {
17144
17209
  const acc = newFilterAccumulator();
17145
17210
  pushScoreIdentity(acc, args.scorerId, args.scoreSource);
17146
17211
  applyScoreFilters(acc, args.filters);
17212
+ const table = qualifiedTable(schema, TABLE_SCORE_EVENTS);
17213
+ applyLatestScorePredicate(acc, table);
17147
17214
  const sql = `
17148
17215
  SELECT ${bucket} AS bucket, ${percentileSelectSql(args.percentiles, "\"score\"")}
17149
- FROM ${qualifiedTable(schema, TABLE_SCORE_EVENTS)}
17216
+ FROM ${table} s
17150
17217
  ${whereOrEmpty(acc)}
17151
17218
  GROUP BY bucket
17152
17219
  ORDER BY bucket
@@ -17345,13 +17412,6 @@ function latestSpanPredicate$1(spanTable) {
17345
17412
  AND (newer."isPending" < s."isPending" OR (newer."isPending" = s."isPending" AND newer."cursorId" > s."cursorId"))
17346
17413
  )`;
17347
17414
  }
17348
- function latestScorePredicate(scoreTable) {
17349
- return `NOT EXISTS (
17350
- SELECT 1 FROM ${scoreTable} newer
17351
- WHERE newer."scoreId" = s."scoreId"
17352
- AND newer."cursorId" > s."cursorId"
17353
- )`;
17354
- }
17355
17415
  function latestFeedbackPredicate(feedbackTable) {
17356
17416
  return `NOT EXISTS (
17357
17417
  SELECT 1 FROM ${feedbackTable} newer
@@ -17521,7 +17581,7 @@ function compilePostgresTraceScope(schema, selection, relationCollections) {
17521
17581
  values
17522
17582
  };
17523
17583
  }
17524
- function compilePostgresTraceQuery(schema, plan) {
17584
+ function compilePostgresTraceQuery(schema, plan, mode = "data") {
17525
17585
  const { ctes, values } = compilePostgresTraceScope(schema, plan, collectRelationCollections(plan.where));
17526
17586
  let predicateSql = "TRUE";
17527
17587
  if (plan.where) {
@@ -17535,6 +17595,12 @@ function compilePostgresTraceQuery(schema, plan) {
17535
17595
  WHERE ${predicateSql}
17536
17596
  )`);
17537
17597
  const candidates = `WITH ${ctes.join(",\n")}`;
17598
+ if (plan.paginationMode === "page" && mode === "count") return {
17599
+ text: `${candidates}
17600
+ SELECT COUNT(*)::text AS count
17601
+ FROM candidates`,
17602
+ values
17603
+ };
17538
17604
  if (plan.result === "groups") {
17539
17605
  const pageCondition = plan.cursor ? `AND "threadId" > $${values.length + 1}` : "";
17540
17606
  if (plan.cursor) values.push(plan.cursor.threadId);
@@ -17552,6 +17618,17 @@ LIMIT $${values.length}`,
17552
17618
  }
17553
17619
  const orderField = plan.orderBy.field === "startedAt" ? "\"startedAt\"" : "\"endedAt\"";
17554
17620
  const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
17621
+ if (plan.paginationMode === "page") {
17622
+ values.push(plan.perPage, plan.page * plan.perPage);
17623
+ return {
17624
+ text: `${candidates}
17625
+ SELECT *
17626
+ FROM candidates
17627
+ ORDER BY ${orderField} ${direction}, "traceId" ASC
17628
+ LIMIT $${values.length - 1} OFFSET $${values.length}`,
17629
+ values
17630
+ };
17631
+ }
17555
17632
  let pageCondition = "";
17556
17633
  if (plan.cursor) {
17557
17634
  const comparison = plan.orderBy.direction === "asc" ? ">" : "<";
@@ -17698,10 +17775,11 @@ function isPostgresResourceLimit(error) {
17698
17775
  const candidate = error;
17699
17776
  return candidate.code === "53200" || candidate.code === "53400";
17700
17777
  }
17701
- async function runWithPostgresTraceQueryTimeout(client, timeoutMs, execute) {
17778
+ async function runWithPostgresTraceQueryTimeout(client, timeoutMs, execute, options = {}) {
17702
17779
  const resolvedTimeoutMs = _mastra_core_storage.resolveTraceQueryTimeoutMs(timeoutMs);
17703
17780
  try {
17704
17781
  return await client.tx(async (transaction) => {
17782
+ if (options.repeatableRead) await transaction.query("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ");
17705
17783
  await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${resolvedTimeoutMs}ms`]);
17706
17784
  return execute(transaction);
17707
17785
  });
@@ -17735,6 +17813,50 @@ async function getTraceQueryValues(client, schema, plan, timeoutMs) {
17735
17813
  });
17736
17814
  }
17737
17815
  async function queryTraces(client, schema, plan, timeoutMs) {
17816
+ if (plan.paginationMode === "page") {
17817
+ const resolvedTimeoutMs = _mastra_core_storage.resolveTraceQueryTimeoutMs(timeoutMs);
17818
+ const deadline = performance.now() + resolvedTimeoutMs;
17819
+ const countQuery = compilePostgresTraceQuery(schema, plan, "count");
17820
+ const dataQuery = compilePostgresTraceQuery(schema, plan);
17821
+ const { total, rows } = await runWithPostgresTraceQueryTimeout(client, resolvedTimeoutMs, async (transaction) => {
17822
+ const countRows = await transaction.any(countQuery.text, countQuery.values);
17823
+ const remainingTimeoutMs = Math.floor(deadline - performance.now());
17824
+ if (remainingTimeoutMs <= 0) throw new _mastra_core_storage.TraceQueryExecutionError();
17825
+ await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${remainingTimeoutMs}ms`]);
17826
+ const rows = await transaction.any(dataQuery.text, dataQuery.values);
17827
+ return {
17828
+ total: Number(countRows[0]?.count ?? 0),
17829
+ rows
17830
+ };
17831
+ }, { repeatableRead: true });
17832
+ const traces = rows.map((row) => ({
17833
+ traceId: String(row.traceId),
17834
+ rootSpanId: String(row.rootSpanId),
17835
+ name: row.name,
17836
+ entityId: row.entityId ?? null,
17837
+ parentSpanId: row.parentSpanId ?? null,
17838
+ createdAt: asIsoTimestamp$1(row.startedAt),
17839
+ metadata: row.metadata ?? null,
17840
+ inputPreview: _mastra_core_storage.buildInputPreview(row.input) ?? null,
17841
+ threadId: row.threadId == null ? null : String(row.threadId),
17842
+ resourceId: row.resourceId == null ? null : String(row.resourceId),
17843
+ startedAt: asIsoTimestamp$1(row.startedAt),
17844
+ endedAt: asIsoTimestamp$1(row.endedAt),
17845
+ entityName: row.entityName == null ? null : String(row.entityName),
17846
+ entityType: row.entityType == null ? null : String(row.entityType),
17847
+ environment: row.environment == null ? null : String(row.environment),
17848
+ status: row.status
17849
+ }));
17850
+ return _mastra_core_storage.traceQueryResponseSchema.parse({
17851
+ traces,
17852
+ pagination: {
17853
+ total,
17854
+ page: plan.page,
17855
+ perPage: plan.perPage,
17856
+ hasMore: (plan.page + 1) * plan.perPage < total
17857
+ }
17858
+ });
17859
+ }
17738
17860
  const query = compilePostgresTraceQuery(schema, plan);
17739
17861
  const rows = await runWithPostgresTraceQueryTimeout(client, timeoutMs, (transaction) => transaction.any(query.text, query.values));
17740
17862
  const visibleRows = rows.slice(0, plan.limit);