@mastra/pg 1.19.0 → 1.20.0-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
@@ -170,13 +170,23 @@ var PGFilterTranslator = class extends _mastra_core_vector_filter.BaseFilterTran
170
170
  };
171
171
  //#endregion
172
172
  //#region src/vector/sql-builder.ts
173
+ const getTextExtractExpr = (key) => {
174
+ const jsonPathKey = parseJsonPathKey(key);
175
+ if (!key.includes(".")) return `metadata->>'${jsonPathKey}'`;
176
+ return `metadata#>>'{${jsonPathKey}}'`;
177
+ };
178
+ const getJsonExtractExpr = (key) => {
179
+ const jsonPathKey = parseJsonPathKey(key);
180
+ if (!key.includes(".")) return `metadata->'${jsonPathKey}'`;
181
+ return `metadata#>'{${jsonPathKey}}'`;
182
+ };
173
183
  const createBasicOperator = (symbol) => {
174
184
  return (key, paramIndex) => {
175
- const jsonPathKey = parseJsonPathKey(key);
185
+ const textExtract = getTextExtractExpr(key);
176
186
  return {
177
- sql: `CASE
178
- WHEN $${paramIndex}::text IS NULL THEN metadata#>>'{${jsonPathKey}}' IS ${symbol === "=" ? "" : "NOT"} NULL
179
- ELSE metadata#>>'{${jsonPathKey}}' ${symbol} $${paramIndex}::text
187
+ sql: `CASE
188
+ WHEN $${paramIndex}::text IS NULL THEN ${textExtract} IS ${symbol === "=" ? "" : "NOT"} NULL
189
+ ELSE ${textExtract} ${symbol} $${paramIndex}::text
180
190
  END`,
181
191
  needsValue: true
182
192
  };
@@ -184,13 +194,14 @@ const createBasicOperator = (symbol) => {
184
194
  };
185
195
  const createNumericOperator = (symbol) => {
186
196
  return (key, paramIndex, value) => {
187
- const jsonPathKey = parseJsonPathKey(key);
197
+ const textExtract = getTextExtractExpr(key);
198
+ const jsonExtract = getJsonExtractExpr(key);
188
199
  if (typeof value === "number" || typeof value === "string" && !isNaN(Number(value)) && value.trim() !== "") return {
189
- sql: `(CASE WHEN jsonb_typeof(metadata#>'{${jsonPathKey}}') = 'number' THEN (metadata#>>'{${jsonPathKey}}')::numeric ${symbol} $${paramIndex}::numeric ELSE NULL END)`,
200
+ sql: `(CASE WHEN jsonb_typeof(${jsonExtract}) = 'number' THEN (${textExtract})::numeric ${symbol} $${paramIndex}::numeric ELSE NULL END)`,
190
201
  needsValue: true
191
202
  };
192
203
  else return {
193
- sql: `metadata#>>'{${jsonPathKey}}' ${symbol} $${paramIndex}::text`,
204
+ sql: `${textExtract} ${symbol} $${paramIndex}::text`,
194
205
  needsValue: true
195
206
  };
196
207
  };
@@ -221,7 +232,7 @@ function buildElemMatchConditions(value, paramIndex) {
221
232
  const operatorFn = FILTER_OPERATORS[paramOperator];
222
233
  if (!operatorFn) throw new Error(`Invalid operator: ${paramOperator}`);
223
234
  const result = operatorFn(paramKey, nextParamIndex, paramValue);
224
- const sql = result.sql.replaceAll("metadata#>>", "elem#>>").replaceAll("metadata#>", "elem#>");
235
+ const sql = result.sql.replaceAll("metadata->>", "elem->>").replaceAll("metadata->", "elem->").replaceAll("metadata#>>", "elem#>>").replaceAll("metadata#>", "elem#>");
225
236
  conditions.push(sql);
226
237
  if (result.needsValue) values.push(paramValue);
227
238
  });
@@ -238,32 +249,34 @@ const FILTER_OPERATORS = {
238
249
  $lt: createNumericOperator("<"),
239
250
  $lte: createNumericOperator("<="),
240
251
  $in: (key, paramIndex) => {
241
- const jsonPathKey = parseJsonPathKey(key);
252
+ const textExtract = getTextExtractExpr(key);
253
+ const jsonExtract = getJsonExtractExpr(key);
242
254
  return {
243
255
  sql: `(
244
256
  CASE
245
- WHEN jsonb_typeof(metadata->'${jsonPathKey}') = 'array' THEN
257
+ WHEN jsonb_typeof(${jsonExtract}) = 'array' THEN
246
258
  EXISTS (
247
- SELECT 1 FROM jsonb_array_elements_text(metadata->'${jsonPathKey}') as elem
259
+ SELECT 1 FROM jsonb_array_elements_text(${jsonExtract}) as elem
248
260
  WHERE elem = ANY($${paramIndex}::text[])
249
261
  )
250
- ELSE metadata#>>'{${jsonPathKey}}' = ANY($${paramIndex}::text[])
262
+ ELSE ${textExtract} = ANY($${paramIndex}::text[])
251
263
  END
252
264
  )`,
253
265
  needsValue: true
254
266
  };
255
267
  },
256
268
  $nin: (key, paramIndex) => {
257
- const jsonPathKey = parseJsonPathKey(key);
269
+ const textExtract = getTextExtractExpr(key);
270
+ const jsonExtract = getJsonExtractExpr(key);
258
271
  return {
259
272
  sql: `(
260
273
  CASE
261
- WHEN jsonb_typeof(metadata->'${jsonPathKey}') = 'array' THEN
274
+ WHEN jsonb_typeof(${jsonExtract}) = 'array' THEN
262
275
  NOT EXISTS (
263
- SELECT 1 FROM jsonb_array_elements_text(metadata->'${jsonPathKey}') as elem
276
+ SELECT 1 FROM jsonb_array_elements_text(${jsonExtract}) as elem
264
277
  WHERE elem = ANY($${paramIndex}::text[])
265
278
  )
266
- ELSE metadata#>>'{${jsonPathKey}}' != ALL($${paramIndex}::text[])
279
+ ELSE ${textExtract} != ALL($${paramIndex}::text[])
267
280
  END
268
281
  )`,
269
282
  needsValue: true
@@ -271,21 +284,21 @@ const FILTER_OPERATORS = {
271
284
  },
272
285
  $all: (key, paramIndex) => {
273
286
  return {
274
- sql: `CASE WHEN array_length($${paramIndex}::text[], 1) IS NULL THEN false
275
- ELSE (metadata#>'{${parseJsonPathKey(key)}}')::jsonb ?& $${paramIndex}::text[] END`,
287
+ sql: `CASE WHEN array_length($${paramIndex}::text[], 1) IS NULL THEN false
288
+ ELSE (${getJsonExtractExpr(key)})::jsonb ?& $${paramIndex}::text[] END`,
276
289
  needsValue: true
277
290
  };
278
291
  },
279
292
  $elemMatch: (key, paramIndex, value) => {
280
293
  const { sql, values } = buildElemMatchConditions(value, paramIndex);
281
- const jsonPathKey = parseJsonPathKey(key);
294
+ const jsonExtract = getJsonExtractExpr(key);
282
295
  return {
283
296
  sql: `(
284
297
  CASE
285
- WHEN jsonb_typeof(metadata->'${jsonPathKey}') = 'array' THEN
298
+ WHEN jsonb_typeof(${jsonExtract}) = 'array' THEN
286
299
  EXISTS (
287
- SELECT 1
288
- FROM jsonb_array_elements(metadata->'${jsonPathKey}') as elem
300
+ SELECT 1
301
+ FROM jsonb_array_elements(${jsonExtract}) as elem
289
302
  WHERE ${sql}
290
303
  )
291
304
  ELSE FALSE
@@ -315,7 +328,7 @@ const FILTER_OPERATORS = {
315
328
  needsValue: false
316
329
  }),
317
330
  $not: (key) => ({
318
- sql: `NOT (${key})`,
331
+ sql: `(${key})`,
319
332
  needsValue: false
320
333
  }),
321
334
  $nor: (key) => ({
@@ -324,16 +337,17 @@ const FILTER_OPERATORS = {
324
337
  }),
325
338
  $regex: (key, paramIndex) => {
326
339
  return {
327
- sql: `metadata#>>'{${parseJsonPathKey(key)}}' ~ $${paramIndex}`,
340
+ sql: `${getTextExtractExpr(key)} ~ $${paramIndex}`,
328
341
  needsValue: true
329
342
  };
330
343
  },
331
344
  $contains: (key, paramIndex, value) => {
332
- const jsonPathKey = parseJsonPathKey(key);
345
+ const textExtract = getTextExtractExpr(key);
346
+ const jsonExtract = getJsonExtractExpr(key);
333
347
  let sql;
334
- if (Array.isArray(value)) sql = `(metadata->'${jsonPathKey}') ?& $${paramIndex}`;
335
- else if (typeof value === "string") sql = `metadata->>'${jsonPathKey}' ILIKE '%' || $${paramIndex} || '%' ESCAPE '\\'`;
336
- else sql = `metadata->>'${jsonPathKey}' = $${paramIndex}`;
348
+ if (Array.isArray(value)) sql = `(${jsonExtract}) ?& $${paramIndex}`;
349
+ else if (typeof value === "string") sql = `${textExtract} ILIKE '%' || $${paramIndex} || '%' ESCAPE '\\'`;
350
+ else sql = `${textExtract} = $${paramIndex}`;
337
351
  return {
338
352
  sql,
339
353
  needsValue: true,
@@ -345,12 +359,12 @@ const FILTER_OPERATORS = {
345
359
  * Usage: { field: { $objectContains: { ...subobject } } }
346
360
  */
347
361
  $size: (key, paramIndex) => {
348
- const jsonPathKey = parseJsonPathKey(key);
362
+ const jsonExtract = getJsonExtractExpr(key);
349
363
  return {
350
364
  sql: `(
351
365
  CASE
352
- WHEN jsonb_typeof(metadata#>'{${jsonPathKey}}') = 'array' THEN
353
- jsonb_array_length(metadata#>'{${jsonPathKey}}') = $${paramIndex}
366
+ WHEN jsonb_typeof(${jsonExtract}) = 'array' THEN
367
+ jsonb_array_length(${jsonExtract}) = $${paramIndex}
354
368
  ELSE FALSE
355
369
  END
356
370
  )`,
@@ -378,7 +392,7 @@ function buildDeleteFilterQuery(filter) {
378
392
  ].includes(key)) return handleLogicalOperator(key, value, parentPath);
379
393
  if (!value || typeof value !== "object") {
380
394
  values.push(value);
381
- return `metadata#>>'{${parseJsonPathKey(key)}}' = $${values.length}`;
395
+ return `${getTextExtractExpr(key)} = $${values.length}`;
382
396
  }
383
397
  const entries = Object.entries(value);
384
398
  if (entries.length > 1) return entries.map(([operator, operatorValue]) => {
@@ -469,7 +483,7 @@ function buildFilterQuery(filter, minScore, topK) {
469
483
  ].includes(key)) return handleLogicalOperator(key, value, parentPath);
470
484
  if (!value || typeof value !== "object") {
471
485
  values.push(value);
472
- return `metadata#>>'{${parseJsonPathKey(key)}}' = $${values.length}`;
486
+ return `${getTextExtractExpr(key)} = $${values.length}`;
473
487
  }
474
488
  const entries = Object.entries(value);
475
489
  if (entries.length > 1) return entries.map(([operator, operatorValue]) => {
@@ -1802,16 +1816,21 @@ var PoolAdapter = class {
1802
1816
  const client = await this.$pool.connect();
1803
1817
  try {
1804
1818
  await client.query("BEGIN");
1805
- const result = await callback(new TransactionClient(client));
1806
- await client.query("COMMIT");
1807
- return result;
1808
- } catch (error) {
1819
+ const txClient = new TransactionClient(client);
1809
1820
  try {
1810
- await client.query("ROLLBACK");
1811
- } catch (rollbackError) {
1812
- console.error("Transaction rollback failed:", rollbackError);
1821
+ const result = await callback(txClient);
1822
+ await txClient.drain();
1823
+ await client.query("COMMIT");
1824
+ return result;
1825
+ } catch (error) {
1826
+ await txClient.drain().catch(() => void 0);
1827
+ try {
1828
+ await client.query("ROLLBACK");
1829
+ } catch (rollbackError) {
1830
+ console.error("Transaction rollback failed:", rollbackError);
1831
+ }
1832
+ throw error;
1813
1833
  }
1814
- throw error;
1815
1834
  } finally {
1816
1835
  client.release();
1817
1836
  }
@@ -1819,41 +1838,85 @@ var PoolAdapter = class {
1819
1838
  };
1820
1839
  /**
1821
1840
  * Transaction client that wraps a PoolClient for executing queries within a transaction.
1841
+ *
1842
+ * Query methods are serialized through a tail promise (same pattern as
1843
+ * PinnedClientAdapter). Callers such as memory.updateMessages historically
1844
+ * did `queries.push(t.none(...))` then `await t.batch(queries)` — each
1845
+ * `t.none()` is async and starts `client.query` immediately, so by the time
1846
+ * batch runs, N queries are already in flight on one PoolClient. pg@8 queues
1847
+ * those internally and emits a DeprecationWarning; pg@9 will throw.
1848
+ * (#20820)
1822
1849
  */
1823
1850
  var TransactionClient = class {
1824
1851
  client;
1852
+ /**
1853
+ * Serialization tail. Without this gate, concurrent t.none()/t.query()
1854
+ * from Promise.all / batch land on the same PoolClient at once.
1855
+ */
1856
+ #tail = Promise.resolve();
1857
+ #error;
1825
1858
  constructor(client) {
1826
1859
  this.client = client;
1827
1860
  }
1828
- async none(query, values) {
1829
- await this.client.query(query, values);
1830
- return null;
1861
+ #enqueue(fn) {
1862
+ const next = this.#tail.then(fn);
1863
+ this.#tail = next.then(() => void 0, (error) => {
1864
+ this.#error ??= { value: error };
1865
+ });
1866
+ return next;
1831
1867
  }
1832
- async one(query, values) {
1833
- const result = await this.client.query(query, values);
1834
- if (result.rows.length === 0) throw new Error(`No data returned from query: ${truncateQuery(query)}`);
1835
- if (result.rows.length > 1) throw new Error(`Multiple rows returned when one was expected: ${truncateQuery(query)}`);
1836
- return result.rows[0];
1868
+ /**
1869
+ * Wait until every enqueued query has settled and surface the first failure.
1870
+ * PoolAdapter / PinnedClientAdapter call this before COMMIT/ROLLBACK so
1871
+ * those control statements never overlap in-flight work on the same client.
1872
+ */
1873
+ async drain() {
1874
+ await this.#tail;
1875
+ if (this.#error) {
1876
+ const { value } = this.#error;
1877
+ this.#error = void 0;
1878
+ throw value;
1879
+ }
1837
1880
  }
1838
- async oneOrNone(query, values) {
1839
- const result = await this.client.query(query, values);
1840
- if (result.rows.length === 0) return null;
1841
- if (result.rows.length > 1) throw new Error(`Multiple rows returned when one or none was expected: ${truncateQuery(query)}`);
1842
- return result.rows[0];
1881
+ none(query, values) {
1882
+ return this.#enqueue(async () => {
1883
+ await this.client.query(query, values);
1884
+ return null;
1885
+ });
1843
1886
  }
1844
- async any(query, values) {
1845
- return (await this.client.query(query, values)).rows;
1887
+ one(query, values) {
1888
+ return this.#enqueue(async () => {
1889
+ const result = await this.client.query(query, values);
1890
+ if (result.rows.length === 0) throw new Error(`No data returned from query: ${truncateQuery(query)}`);
1891
+ if (result.rows.length > 1) throw new Error(`Multiple rows returned when one was expected: ${truncateQuery(query)}`);
1892
+ return result.rows[0];
1893
+ });
1846
1894
  }
1847
- async manyOrNone(query, values) {
1895
+ oneOrNone(query, values) {
1896
+ return this.#enqueue(async () => {
1897
+ const result = await this.client.query(query, values);
1898
+ if (result.rows.length === 0) return null;
1899
+ if (result.rows.length > 1) throw new Error(`Multiple rows returned when one or none was expected: ${truncateQuery(query)}`);
1900
+ return result.rows[0];
1901
+ });
1902
+ }
1903
+ any(query, values) {
1904
+ return this.#enqueue(async () => {
1905
+ return (await this.client.query(query, values)).rows;
1906
+ });
1907
+ }
1908
+ manyOrNone(query, values) {
1848
1909
  return this.any(query, values);
1849
1910
  }
1850
- async many(query, values) {
1851
- const result = await this.client.query(query, values);
1852
- if (result.rows.length === 0) throw new Error(`No data returned from query: ${truncateQuery(query)}`);
1853
- return result.rows;
1911
+ many(query, values) {
1912
+ return this.#enqueue(async () => {
1913
+ const result = await this.client.query(query, values);
1914
+ if (result.rows.length === 0) throw new Error(`No data returned from query: ${truncateQuery(query)}`);
1915
+ return result.rows;
1916
+ });
1854
1917
  }
1855
- async query(query, values) {
1856
- return this.client.query(query, values);
1918
+ query(query, values) {
1919
+ return this.#enqueue(() => this.client.query(query, values));
1857
1920
  }
1858
1921
  async batch(promises) {
1859
1922
  return Promise.all(promises);
@@ -1946,11 +2009,14 @@ var PinnedClientAdapter = class {
1946
2009
  tx(callback) {
1947
2010
  return this.#enqueue(async () => {
1948
2011
  await this.pinnedClient.query("BEGIN");
2012
+ const txClient = new TransactionClient(this.pinnedClient);
1949
2013
  try {
1950
- const result = await callback(new TransactionClient(this.pinnedClient));
2014
+ const result = await callback(txClient);
2015
+ await txClient.drain();
1951
2016
  await this.pinnedClient.query("COMMIT");
1952
2017
  return result;
1953
2018
  } catch (error) {
2019
+ await txClient.drain().catch(() => void 0);
1954
2020
  try {
1955
2021
  await this.pinnedClient.query("ROLLBACK");
1956
2022
  } catch (rollbackError) {
@@ -6440,7 +6506,13 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
6440
6506
  ifNotExists: [
6441
6507
  "agentVersion",
6442
6508
  "organizationId",
6443
- "projectId"
6509
+ "projectId",
6510
+ "provenance",
6511
+ "runnerAttestation",
6512
+ "experimentSetId",
6513
+ "comparisonId",
6514
+ "variantId",
6515
+ "trialIndex"
6444
6516
  ]
6445
6517
  });
6446
6518
  await this.#db.alterTable({
@@ -6546,6 +6618,16 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
6546
6618
  table: _mastra_core_storage.TABLE_EXPERIMENTS,
6547
6619
  columns: ["datasetId"]
6548
6620
  },
6621
+ {
6622
+ name: "idx_experiments_grouping",
6623
+ table: _mastra_core_storage.TABLE_EXPERIMENTS,
6624
+ columns: [
6625
+ "experimentSetId",
6626
+ "comparisonId",
6627
+ "variantId",
6628
+ "trialIndex"
6629
+ ]
6630
+ },
6549
6631
  {
6550
6632
  name: "idx_experiment_results_experimentid",
6551
6633
  table: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS,
@@ -6591,6 +6673,12 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
6591
6673
  name: row.name ?? void 0,
6592
6674
  description: row.description ?? void 0,
6593
6675
  metadata: row.metadata ? (0, _mastra_core_storage.safelyParseJSON)(row.metadata) : void 0,
6676
+ provenance: row.provenance ? (0, _mastra_core_storage.safelyParseJSON)(row.provenance) : null,
6677
+ runnerAttestation: row.runnerAttestation ? (0, _mastra_core_storage.safelyParseJSON)(row.runnerAttestation) : null,
6678
+ experimentSetId: row.experimentSetId ?? null,
6679
+ comparisonId: row.comparisonId ?? null,
6680
+ variantId: row.variantId ?? null,
6681
+ trialIndex: row.trialIndex != null ? row.trialIndex : null,
6594
6682
  datasetId: row.datasetId ?? null,
6595
6683
  datasetVersion: row.datasetVersion != null ? row.datasetVersion : null,
6596
6684
  agentVersion: row.agentVersion ?? null,
@@ -6644,6 +6732,12 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
6644
6732
  name: input.name ?? null,
6645
6733
  description: input.description ?? null,
6646
6734
  metadata: input.metadata ?? null,
6735
+ provenance: input.provenance ?? null,
6736
+ runnerAttestation: input.runnerAttestation ?? null,
6737
+ experimentSetId: input.experimentSetId ?? null,
6738
+ comparisonId: input.comparisonId ?? null,
6739
+ variantId: input.variantId ?? null,
6740
+ trialIndex: input.trialIndex ?? null,
6647
6741
  datasetId: input.datasetId ?? null,
6648
6742
  datasetVersion: input.datasetVersion ?? null,
6649
6743
  agentVersion: input.agentVersion ?? null,
@@ -6667,6 +6761,12 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
6667
6761
  name: input.name,
6668
6762
  description: input.description,
6669
6763
  metadata: input.metadata,
6764
+ provenance: input.provenance ?? null,
6765
+ runnerAttestation: input.runnerAttestation ?? null,
6766
+ experimentSetId: input.experimentSetId ?? null,
6767
+ comparisonId: input.comparisonId ?? null,
6768
+ variantId: input.variantId ?? null,
6769
+ trialIndex: input.trialIndex ?? null,
6670
6770
  datasetId: input.datasetId ?? null,
6671
6771
  datasetVersion: input.datasetVersion ?? null,
6672
6772
  agentVersion: input.agentVersion ?? null,
@@ -6808,6 +6908,22 @@ var ExperimentsPG = class ExperimentsPG extends _mastra_core_storage.Experiments
6808
6908
  conditions.push(`"status" = $${paramIndex++}`);
6809
6909
  queryParams.push(args.status);
6810
6910
  }
6911
+ if (args.experimentSetId !== void 0) {
6912
+ conditions.push(`"experimentSetId" = $${paramIndex++}`);
6913
+ queryParams.push(args.experimentSetId);
6914
+ }
6915
+ if (args.comparisonId !== void 0) {
6916
+ conditions.push(`"comparisonId" = $${paramIndex++}`);
6917
+ queryParams.push(args.comparisonId);
6918
+ }
6919
+ if (args.variantId !== void 0) {
6920
+ conditions.push(`"variantId" = $${paramIndex++}`);
6921
+ queryParams.push(args.variantId);
6922
+ }
6923
+ if (args.trialIndex !== void 0) {
6924
+ conditions.push(`"trialIndex" = $${paramIndex++}`);
6925
+ queryParams.push(args.trialIndex);
6926
+ }
6811
6927
  if (args.filters) {
6812
6928
  const { organizationId, projectId } = args.filters;
6813
6929
  if (organizationId !== void 0) {
@@ -8589,6 +8705,13 @@ function getTableName$3({ indexName, schemaName }) {
8589
8705
  function inPlaceholders(count, startIndex = 1) {
8590
8706
  return Array.from({ length: count }, (_, i) => `$${i + startIndex}`).join(", ");
8591
8707
  }
8708
+ /**
8709
+ * Bind dates as UTC strings because node-postgres serializes Date parameters
8710
+ * for TIMESTAMP columns using the process's local timezone.
8711
+ */
8712
+ function toUtcISOString(date) {
8713
+ return date.toISOString();
8714
+ }
8592
8715
  function dedupeMessagesForSave(messages) {
8593
8716
  const deduped = /* @__PURE__ */ new Map();
8594
8717
  for (const message of messages) {
@@ -9003,6 +9126,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9003
9126
  hasMore: perPageInput === false ? false : offset + perPage < total
9004
9127
  };
9005
9128
  } catch (error) {
9129
+ if (error instanceof _mastra_core_error.MastraError && error.category === _mastra_core_error.ErrorCategory.USER) throw error;
9006
9130
  const mastraError = new _mastra_core_error.MastraError({
9007
9131
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "LIST_THREADS", "FAILED"),
9008
9132
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -9015,13 +9139,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9015
9139
  }, error);
9016
9140
  this.logger?.error?.(mastraError.toString());
9017
9141
  this.logger?.trackException(mastraError);
9018
- return {
9019
- threads: [],
9020
- total: 0,
9021
- page,
9022
- perPage: perPageForResponse,
9023
- hasMore: false
9024
- };
9142
+ throw mastraError;
9025
9143
  }
9026
9144
  }
9027
9145
  async saveThread({ thread }) {
@@ -9030,6 +9148,8 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9030
9148
  indexName: _mastra_core_storage.TABLE_THREADS,
9031
9149
  schemaName: getSchemaName$3(this.#schema)
9032
9150
  });
9151
+ const createdAt = toUtcISOString(thread.createdAt);
9152
+ const updatedAt = toUtcISOString(thread.updatedAt);
9033
9153
  await this.#db.client.none(`INSERT INTO ${tableName} (
9034
9154
  id,
9035
9155
  "resourceId",
@@ -9052,10 +9172,10 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9052
9172
  thread.resourceId,
9053
9173
  thread.title,
9054
9174
  thread.metadata ? JSON.stringify(thread.metadata) : null,
9055
- thread.createdAt,
9056
- thread.createdAt,
9057
- thread.updatedAt,
9058
- thread.updatedAt
9175
+ createdAt,
9176
+ createdAt,
9177
+ updatedAt,
9178
+ updatedAt
9059
9179
  ]);
9060
9180
  return thread;
9061
9181
  } catch (error) {
@@ -9088,7 +9208,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9088
9208
  ...metadata
9089
9209
  };
9090
9210
  try {
9091
- const now = /* @__PURE__ */ new Date();
9211
+ const nowStr = toUtcISOString(/* @__PURE__ */ new Date());
9092
9212
  const thread = await this.#db.client.one(`UPDATE ${threadTableName}
9093
9213
  SET
9094
9214
  title = $1,
@@ -9100,8 +9220,8 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9100
9220
  `, [
9101
9221
  title,
9102
9222
  mergedMetadata,
9103
- now,
9104
- now,
9223
+ nowStr,
9224
+ nowStr,
9105
9225
  id
9106
9226
  ]);
9107
9227
  return {
@@ -9290,9 +9410,41 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9290
9410
  }, error);
9291
9411
  this.logger?.error?.(mastraError.toString());
9292
9412
  this.logger?.trackException(mastraError);
9293
- return { messages: [] };
9413
+ throw mastraError;
9294
9414
  }
9295
9415
  }
9416
+ /**
9417
+ * Reads one page of messages together with the total row count.
9418
+ *
9419
+ * `COUNT(*) OVER ()` reports the count over the whole WHERE result on the same
9420
+ * statement as the page, so the page costs one database round-trip instead of
9421
+ * two. The page and the count also come from one snapshot, so the count always
9422
+ * describes the returned rows. A separate `COUNT(*)` runs only when the page is
9423
+ * empty and the caller asked for a page after the last row, because a window
9424
+ * function has no row to carry the count on.
9425
+ */
9426
+ async #fetchMessagePage({ selectStatement, tableName, whereClause, orderByStatement, queryParams, perPageInput, perPage, offset }) {
9427
+ const limitClause = perPageInput === false ? "" : ` LIMIT $${queryParams.length + 1} OFFSET $${queryParams.length + 2}`;
9428
+ const dataParams = perPageInput === false ? queryParams : [
9429
+ ...queryParams,
9430
+ perPage,
9431
+ offset
9432
+ ];
9433
+ const rows = await this.#db.client.manyOrNone(`${selectStatement}, COUNT(*) OVER () AS "__total" FROM ${tableName} ${whereClause} ${orderByStatement}${limitClause}`, dataParams) || [];
9434
+ if (rows.length > 0) return {
9435
+ total: Number(rows[0].__total),
9436
+ messages: rows
9437
+ };
9438
+ if (offset === 0) return {
9439
+ total: 0,
9440
+ messages: []
9441
+ };
9442
+ const countResult = await this.#db.client.one(`SELECT COUNT(*) FROM ${tableName} ${whereClause}`, queryParams);
9443
+ return {
9444
+ total: parseInt(countResult.count, 10),
9445
+ messages: []
9446
+ };
9447
+ }
9296
9448
  async listMessages(args) {
9297
9449
  const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
9298
9450
  const threadIds = (Array.isArray(threadId) ? threadId : [threadId]).filter((id) => typeof id === "string");
@@ -9367,23 +9519,27 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9367
9519
  hasMore: false
9368
9520
  };
9369
9521
  }
9522
+ let includeFailure;
9523
+ const includePromise = include && include.length > 0 ? this._getIncludedMessages({ include }).catch((error) => {
9524
+ includeFailure = error;
9525
+ return null;
9526
+ }) : null;
9370
9527
  let total;
9371
9528
  let messages;
9372
9529
  if (metadataFilter) {
9373
9530
  const filteredRows = (await this.#db.client.manyOrNone(`${selectStatement} FROM ${tableName} ${whereClause} ${orderByStatement}`, queryParams) || []).filter((row) => (0, _mastra_core_storage.storageMessageMatchesMetadataFilter)(row.content, metadataFilter));
9374
9531
  total = filteredRows.length;
9375
9532
  messages = perPageInput === false ? filteredRows : filteredRows.slice(offset, offset + perPage);
9376
- } else {
9377
- const countResult = await this.#db.client.one(`SELECT COUNT(*) FROM ${tableName} ${whereClause}`, queryParams);
9378
- total = parseInt(countResult.count, 10);
9379
- const limitValue = perPageInput === false ? total : perPage;
9380
- const dataQuery = `${selectStatement} FROM ${tableName} ${whereClause} ${orderByStatement} LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
9381
- messages = [...await this.#db.client.manyOrNone(dataQuery, [
9382
- ...queryParams,
9383
- limitValue,
9384
- offset
9385
- ]) || []];
9386
- }
9533
+ } else ({total, messages} = await this.#fetchMessagePage({
9534
+ selectStatement,
9535
+ tableName,
9536
+ whereClause,
9537
+ orderByStatement,
9538
+ queryParams,
9539
+ perPageInput,
9540
+ perPage,
9541
+ offset
9542
+ }));
9387
9543
  const primaryPageCount = messages.length;
9388
9544
  if (total === 0 && messages.length === 0 && (!include || include.length === 0)) return {
9389
9545
  messages: [],
@@ -9394,7 +9550,8 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9394
9550
  };
9395
9551
  const messageIds = new Set(messages.map((m) => m.id));
9396
9552
  if (include && include.length > 0) {
9397
- const includeMessages = await this._getIncludedMessages({ include });
9553
+ const includeMessages = await includePromise;
9554
+ if (includeFailure) throw includeFailure;
9398
9555
  if (includeMessages) {
9399
9556
  for (const includeMsg of includeMessages) if (!messageIds.has(includeMsg.id)) {
9400
9557
  messages.push(includeMsg);
@@ -9416,6 +9573,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9416
9573
  hasMore
9417
9574
  };
9418
9575
  } catch (error) {
9576
+ if (error instanceof _mastra_core_error.MastraError && error.category === _mastra_core_error.ErrorCategory.USER) throw error;
9419
9577
  const mastraError = new _mastra_core_error.MastraError({
9420
9578
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "LIST_MESSAGES", "FAILED"),
9421
9579
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -9427,13 +9585,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9427
9585
  }, error);
9428
9586
  this.logger?.error?.(mastraError.toString());
9429
9587
  this.logger?.trackException(mastraError);
9430
- return {
9431
- messages: [],
9432
- total: 0,
9433
- page,
9434
- perPage: perPageForResponse,
9435
- hasMore: false
9436
- };
9588
+ throw mastraError;
9437
9589
  }
9438
9590
  }
9439
9591
  async listMessagesByResourceId(args) {
@@ -9507,23 +9659,27 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9507
9659
  hasMore: false
9508
9660
  };
9509
9661
  }
9662
+ let includeFailure;
9663
+ const includePromise = include && include.length > 0 ? this._getIncludedMessages({ include }).catch((error) => {
9664
+ includeFailure = error;
9665
+ return null;
9666
+ }) : null;
9510
9667
  let total;
9511
9668
  let messages;
9512
9669
  if (metadataFilter) {
9513
9670
  const filteredRows = (await this.#db.client.manyOrNone(`${selectStatement} FROM ${tableName} ${whereClause} ${orderByStatement}`, queryParams) || []).filter((row) => (0, _mastra_core_storage.storageMessageMatchesMetadataFilter)(row.content, metadataFilter));
9514
9671
  total = filteredRows.length;
9515
9672
  messages = perPageInput === false ? filteredRows : filteredRows.slice(offset, offset + perPage);
9516
- } else {
9517
- const countResult = await this.#db.client.one(`SELECT COUNT(*) FROM ${tableName} ${whereClause}`, queryParams);
9518
- total = parseInt(countResult.count, 10);
9519
- const limitValue = perPageInput === false ? total : perPage;
9520
- const dataQuery = `${selectStatement} FROM ${tableName} ${whereClause} ${orderByStatement} LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
9521
- messages = [...await this.#db.client.manyOrNone(dataQuery, [
9522
- ...queryParams,
9523
- limitValue,
9524
- offset
9525
- ]) || []];
9526
- }
9673
+ } else ({total, messages} = await this.#fetchMessagePage({
9674
+ selectStatement,
9675
+ tableName,
9676
+ whereClause,
9677
+ orderByStatement,
9678
+ queryParams,
9679
+ perPageInput,
9680
+ perPage,
9681
+ offset
9682
+ }));
9527
9683
  if (total === 0 && messages.length === 0 && (!include || include.length === 0)) return {
9528
9684
  messages: [],
9529
9685
  total: 0,
@@ -9533,7 +9689,8 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9533
9689
  };
9534
9690
  const messageIds = new Set(messages.map((m) => m.id));
9535
9691
  if (include && include.length > 0) {
9536
- const includeMessages = await this._getIncludedMessages({ include });
9692
+ const includeMessages = await includePromise;
9693
+ if (includeFailure) throw includeFailure;
9537
9694
  if (includeMessages) {
9538
9695
  for (const includeMsg of includeMessages) if (!messageIds.has(includeMsg.id)) {
9539
9696
  messages.push(includeMsg);
@@ -9553,6 +9710,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9553
9710
  hasMore
9554
9711
  };
9555
9712
  } catch (error) {
9713
+ if (error instanceof _mastra_core_error.MastraError && error.category === _mastra_core_error.ErrorCategory.USER) throw error;
9556
9714
  const mastraError = new _mastra_core_error.MastraError({
9557
9715
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "LIST_MESSAGES_BY_RESOURCE_ID", "FAILED"),
9558
9716
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -9561,13 +9719,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9561
9719
  }, error);
9562
9720
  this.logger?.error?.(mastraError.toString());
9563
9721
  this.logger?.trackException(mastraError);
9564
- return {
9565
- messages: [],
9566
- total: 0,
9567
- page,
9568
- perPage: perPageForResponse,
9569
- hasMore: false
9570
- };
9722
+ throw mastraError;
9571
9723
  }
9572
9724
  }
9573
9725
  async saveMessages({ messages }) {
@@ -9603,7 +9755,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9603
9755
  const batch = messagesToSave.slice(offset, offset + MAX_MESSAGES_PER_INSERT);
9604
9756
  const values = [];
9605
9757
  const valuePlaceholders = batch.map((message, messageIndex) => {
9606
- const createdAt = message.createdAt || /* @__PURE__ */ new Date();
9758
+ const createdAt = toUtcISOString(message.createdAt || /* @__PURE__ */ new Date());
9607
9759
  values.push(message.id, message.threadId, typeof message.content === "string" ? message.content : JSON.stringify(message.content), createdAt, createdAt, message.role, message.type || "v2", message.resourceId);
9608
9760
  const paramOffset = messageIndex * MESSAGE_INSERT_BIND_PARAMETERS;
9609
9761
  return `(${Array.from({ length: MESSAGE_INSERT_BIND_PARAMETERS }, (_, paramIndex) => `$${paramOffset + paramIndex + 1}`).join(", ")})`;
@@ -9621,7 +9773,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9621
9773
  indexName: _mastra_core_storage.TABLE_THREADS,
9622
9774
  schemaName: getSchemaName$3(this.#schema)
9623
9775
  });
9624
- const now = /* @__PURE__ */ new Date();
9776
+ const now = toUtcISOString(/* @__PURE__ */ new Date());
9625
9777
  for (const threadIdToUpdate of threadIds) await t.none(`UPDATE ${threadTableName}
9626
9778
  SET
9627
9779
  "updatedAt" = $1,
@@ -9773,11 +9925,15 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9773
9925
  };
9774
9926
  }
9775
9927
  async saveResource({ resource }) {
9928
+ const createdAt = toUtcISOString(resource.createdAt);
9929
+ const updatedAt = toUtcISOString(resource.updatedAt);
9776
9930
  await this.#db.insert({
9777
9931
  tableName: _mastra_core_storage.TABLE_RESOURCES,
9778
9932
  record: {
9779
9933
  ...resource,
9780
- metadata: JSON.stringify(resource.metadata)
9934
+ metadata: JSON.stringify(resource.metadata),
9935
+ createdAt,
9936
+ updatedAt
9781
9937
  }
9782
9938
  });
9783
9939
  return resource;
@@ -9881,6 +10037,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9881
10037
  }
9882
10038
  const sourceMessages = await t.manyOrNone(messageQuery, messageParams);
9883
10039
  const now = /* @__PURE__ */ new Date();
10040
+ const nowStr = toUtcISOString(now);
9884
10041
  const lastMessageId = sourceMessages.length > 0 ? sourceMessages[sourceMessages.length - 1].id : void 0;
9885
10042
  const cloneMetadata = {
9886
10043
  sourceThreadId,
@@ -9912,10 +10069,10 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9912
10069
  newThread.resourceId,
9913
10070
  newThread.title,
9914
10071
  newThread.metadata ? JSON.stringify(newThread.metadata) : null,
9915
- now,
9916
- now,
9917
- now,
9918
- now
10072
+ nowStr,
10073
+ nowStr,
10074
+ nowStr,
10075
+ nowStr
9919
10076
  ]);
9920
10077
  const clonedMessages = [];
9921
10078
  const messageIdMap = {};
@@ -9928,13 +10085,14 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
9928
10085
  try {
9929
10086
  parsedContent = JSON.parse(normalizedMsg.content);
9930
10087
  } catch {}
10088
+ const createdAt = toUtcISOString(new Date(normalizedMsg.createdAt));
9931
10089
  await t.none(`INSERT INTO ${messageTableName} (id, thread_id, content, "createdAt", "createdAtZ", role, type, "resourceId")
9932
10090
  VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, [
9933
10091
  newMessageId,
9934
10092
  newThreadId,
9935
10093
  typeof normalizedMsg.content === "string" ? normalizedMsg.content : JSON.stringify(normalizedMsg.content),
9936
- normalizedMsg.createdAt,
9937
- normalizedMsg.createdAt,
10094
+ createdAt,
10095
+ createdAt,
9938
10096
  normalizedMsg.role,
9939
10097
  normalizedMsg.type || "v2",
9940
10098
  targetResourceId