@mastra/pg 1.36.0-alpha.3 → 1.36.0-alpha.5

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.js CHANGED
@@ -250,31 +250,29 @@ function buildElemMatchConditions(value, paramIndex) {
250
250
  if (typeof value !== "object" || Array.isArray(value)) throw new Error("$elemMatch requires an object with conditions");
251
251
  const conditions = [];
252
252
  const values = [];
253
+ function buildFieldCondition(field, operator, operatorValue) {
254
+ if (operator === "$not") {
255
+ const nested = Object.entries(operatorValue);
256
+ if (nested.length === 0) throw new Error("$not requires at least one operator");
257
+ return `NOT (COALESCE(${nested.map(([nestedOp, nestedValue]) => buildFieldCondition(field, nestedOp, nestedValue)).join(" AND ")}, FALSE))`;
258
+ }
259
+ const operatorFn = FILTER_OPERATORS[operator];
260
+ if (!operatorFn) throw new Error(`Invalid operator: ${operator}`);
261
+ const scalarElement = field.startsWith("$");
262
+ const result = operatorFn(scalarElement ? "" : field, paramIndex + values.length, operatorValue);
263
+ const sql = result.sql.replaceAll("metadata->>", "elem->>").replaceAll("metadata->", "elem->").replaceAll("metadata#>>", "elem#>>").replaceAll("metadata#>", "elem#>").replaceAll("metadata ?", "elem ?");
264
+ const elementSql = scalarElement ? sql.replaceAll("elem->>''", "elem #>> '{}'").replaceAll("elem->''", "elem") : sql;
265
+ if (result.needsValue) {
266
+ const transformedValue = result.transformValue ? result.transformValue() : operatorValue;
267
+ if (operator === "$elemMatch") values.push(...transformedValue);
268
+ else values.push(transformedValue);
269
+ }
270
+ return elementSql;
271
+ }
253
272
  Object.entries(value).forEach(([field, val]) => {
254
- const nextParamIndex = paramIndex + values.length;
255
- let paramOperator;
256
- let paramKey;
257
- let paramValue;
258
- if (field.startsWith("$")) {
259
- paramOperator = field;
260
- paramKey = "";
261
- paramValue = val;
262
- } else if (typeof val === "object" && !Array.isArray(val)) {
263
- const [op, opValue] = Object.entries(val || {})[0] || [];
264
- paramOperator = op;
265
- paramKey = field;
266
- paramValue = opValue;
267
- } else {
268
- paramOperator = "$eq";
269
- paramKey = field;
270
- paramValue = val;
271
- }
272
- const operatorFn = FILTER_OPERATORS[paramOperator];
273
- if (!operatorFn) throw new Error(`Invalid operator: ${paramOperator}`);
274
- const result = operatorFn(paramKey, nextParamIndex, paramValue);
275
- const sql = result.sql.replaceAll("metadata->>", "elem->>").replaceAll("metadata->", "elem->").replaceAll("metadata#>>", "elem#>>").replaceAll("metadata#>", "elem#>");
276
- conditions.push(sql);
277
- if (result.needsValue) values.push(paramValue);
273
+ const operators = field.startsWith("$") ? [[field, val]] : typeof val === "object" && val !== null && !Array.isArray(val) ? Object.entries(val) : [["$eq", val]];
274
+ if (operators.length === 0) throw new Error(`$elemMatch field ${field} requires at least one operator`);
275
+ for (const [operator, operatorValue] of operators) conditions.push(buildFieldCondition(field, operator, operatorValue));
278
276
  });
279
277
  return {
280
278
  sql: conditions.join(" AND "),
@@ -2068,8 +2066,10 @@ function truncateQuery(query, maxLength = 100) {
2068
2066
  */
2069
2067
  var PoolAdapter = class {
2070
2068
  $pool;
2071
- constructor($pool) {
2069
+ onRollbackError;
2070
+ constructor($pool, onRollbackError) {
2072
2071
  this.$pool = $pool;
2072
+ this.onRollbackError = onRollbackError;
2073
2073
  }
2074
2074
  connect() {
2075
2075
  return this.$pool.connect();
@@ -2106,8 +2106,14 @@ var PoolAdapter = class {
2106
2106
  }
2107
2107
  async tx(callback) {
2108
2108
  const client = await this.$pool.connect();
2109
+ let destroyClient = false;
2109
2110
  try {
2110
- await client.query("BEGIN");
2111
+ try {
2112
+ await client.query("BEGIN");
2113
+ } catch (error) {
2114
+ destroyClient = true;
2115
+ throw error;
2116
+ }
2111
2117
  const txClient = new TransactionClient(client);
2112
2118
  try {
2113
2119
  const result = await callback(txClient);
@@ -2119,12 +2125,16 @@ var PoolAdapter = class {
2119
2125
  try {
2120
2126
  await client.query("ROLLBACK");
2121
2127
  } catch (rollbackError) {
2122
- console.error("Transaction rollback failed:", rollbackError);
2128
+ destroyClient = true;
2129
+ try {
2130
+ if (this.onRollbackError) await this.onRollbackError(rollbackError);
2131
+ else console.error("Failed to rollback transaction:", rollbackError);
2132
+ } catch {}
2123
2133
  }
2124
2134
  throw error;
2125
2135
  }
2126
2136
  } finally {
2127
- client.release();
2137
+ client.release(destroyClient || void 0);
2128
2138
  }
2129
2139
  }
2130
2140
  };
@@ -2231,6 +2241,7 @@ var TransactionClient = class {
2231
2241
  var PinnedClientAdapter = class {
2232
2242
  $pool;
2233
2243
  pinnedClient;
2244
+ onRollbackError;
2234
2245
  /**
2235
2246
  * Serialization tail. Domain init() methods fire via Promise.all in
2236
2247
  * MastraCompositeStore.#runInit(), so without our own gate every domain's
@@ -2241,9 +2252,14 @@ var PinnedClientAdapter = class {
2241
2252
  * the reliance on pg's internal queue.
2242
2253
  */
2243
2254
  #tail = Promise.resolve();
2244
- constructor($pool, pinnedClient) {
2255
+ #rollbackError;
2256
+ constructor($pool, pinnedClient, onRollbackError) {
2245
2257
  this.$pool = $pool;
2246
2258
  this.pinnedClient = pinnedClient;
2259
+ this.onRollbackError = onRollbackError;
2260
+ }
2261
+ get shouldDestroyClient() {
2262
+ return this.#rollbackError !== void 0;
2247
2263
  }
2248
2264
  /**
2249
2265
  * Run `fn` after any previously enqueued work on this pinned client has
@@ -2251,7 +2267,11 @@ var PinnedClientAdapter = class {
2251
2267
  * resume on the next caller's turn.
2252
2268
  */
2253
2269
  #enqueue(fn) {
2254
- const next = this.#tail.then(fn, fn);
2270
+ const run = async () => {
2271
+ if (this.shouldDestroyClient) throw this.#rollbackError;
2272
+ return fn();
2273
+ };
2274
+ const next = this.#tail.then(run, run);
2255
2275
  this.#tail = next.catch(() => void 0);
2256
2276
  return next;
2257
2277
  }
@@ -2303,7 +2323,12 @@ var PinnedClientAdapter = class {
2303
2323
  }
2304
2324
  tx(callback) {
2305
2325
  return this.#enqueue(async () => {
2306
- await this.pinnedClient.query("BEGIN");
2326
+ try {
2327
+ await this.pinnedClient.query("BEGIN");
2328
+ } catch (error) {
2329
+ this.#rollbackError = error;
2330
+ throw error;
2331
+ }
2307
2332
  const txClient = new TransactionClient(this.pinnedClient);
2308
2333
  try {
2309
2334
  const result = await callback(txClient);
@@ -2315,7 +2340,11 @@ var PinnedClientAdapter = class {
2315
2340
  try {
2316
2341
  await this.pinnedClient.query("ROLLBACK");
2317
2342
  } catch (rollbackError) {
2318
- console.error("Transaction rollback failed:", rollbackError);
2343
+ this.#rollbackError = rollbackError;
2344
+ try {
2345
+ if (this.onRollbackError) await this.onRollbackError(rollbackError);
2346
+ else console.error("Failed to rollback transaction:", rollbackError);
2347
+ } catch {}
2319
2348
  }
2320
2349
  throw error;
2321
2350
  }
@@ -3405,12 +3434,16 @@ var PgDB = class extends MastraBase {
3405
3434
  }
3406
3435
  if (snapshot) {
3407
3436
  snapshot.tables.add(tableName);
3408
- if (isNewTable) snapshot.createdTables.add(tableName);
3409
- const created = this.snapshotColumns(snapshot, tableName);
3410
- for (const [name, def] of Object.entries(schema)) {
3411
- const parsedName = parseSqlIdentifier(name, "column name");
3412
- created.add(parsedName);
3413
- if (def.type === "timestamp") created.add(`${parsedName}Z`);
3437
+ if (isNewTable) {
3438
+ snapshot.createdTables.add(tableName);
3439
+ snapshot.columns.set(tableName, new Set(await this.getTableColumns(tableName)));
3440
+ } else {
3441
+ const created = this.snapshotColumns(snapshot, tableName);
3442
+ for (const [name, def] of Object.entries(schema)) {
3443
+ const parsedName = parseSqlIdentifier(name, "column name");
3444
+ created.add(parsedName);
3445
+ if (def.type === "timestamp") created.add(`${parsedName}Z`);
3446
+ }
3414
3447
  }
3415
3448
  }
3416
3449
  }
@@ -11799,15 +11832,25 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
11799
11832
  * @param schemaPrefix - Prefix for index names (e.g. "my_schema_" or "")
11800
11833
  */
11801
11834
  static getDefaultIndexDefs(schemaPrefix) {
11802
- return [{
11803
- name: `${schemaPrefix}mastra_threads_resourceid_createdat_idx`,
11804
- table: TABLE_THREADS,
11805
- columns: ["resourceId", "createdAt DESC"]
11806
- }, {
11807
- name: `${schemaPrefix}mastra_messages_thread_id_createdat_idx`,
11808
- table: TABLE_MESSAGES,
11809
- columns: ["thread_id", "createdAt DESC"]
11810
- }];
11835
+ return [
11836
+ {
11837
+ name: `${schemaPrefix}mastra_threads_resourceid_createdat_idx`,
11838
+ table: TABLE_THREADS,
11839
+ columns: ["resourceId", "createdAt DESC"]
11840
+ },
11841
+ {
11842
+ name: `${schemaPrefix}mastra_threads_metadata_gin_idx`,
11843
+ table: TABLE_THREADS,
11844
+ columns: ["metadata"],
11845
+ method: "gin",
11846
+ opclass: "jsonb_path_ops"
11847
+ },
11848
+ {
11849
+ name: `${schemaPrefix}mastra_messages_thread_id_createdat_idx`,
11850
+ table: TABLE_MESSAGES,
11851
+ columns: ["thread_id", "createdAt DESC"]
11852
+ }
11853
+ ];
11811
11854
  }
11812
11855
  /**
11813
11856
  * Returns all DDL statements for this domain: tables (threads, messages, resources, OM), indexes.
@@ -21926,6 +21969,7 @@ async function dangerouslyClearTracing(client, schema) {
21926
21969
  const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? [
21927
21970
  "span-query",
21928
21971
  "span-query-context-fields",
21972
+ ...coreStorage.SPAN_QUERY_ORDER_FIELDS?.includes("durationMs") ? ["span-query-duration-sort"] : [],
21929
21973
  ..."model" in (coreStorage.SPAN_QUERY_FIELD_REGISTRY ?? {}) ? ["span-query-model-fields"] : []
21930
21974
  ] : [];
21931
21975
  const spanAggregateFeatures = typeof coreStorage.planSpanAggregate === "function" ? ["span-aggregate", ..."model" in (coreStorage.SPAN_AGGREGATE_DIMENSION_REGISTRY ?? {}) ? ["span-aggregate-model-dimensions"] : []] : [];
@@ -22997,6 +23041,8 @@ function rowToSchedule(row) {
22997
23041
  if (metadata !== void 0) schedule.metadata = metadata;
22998
23042
  if (row.owner_type != null) schedule.ownerType = String(row.owner_type);
22999
23043
  if (row.owner_id != null) schedule.ownerId = String(row.owner_id);
23044
+ if (row.run_at != null) schedule.runAt = Number(row.run_at);
23045
+ if (row.end_at != null) schedule.endAt = Number(row.end_at);
23000
23046
  return schedule;
23001
23047
  }
23002
23048
  function rowToTrigger(row) {
@@ -23056,6 +23102,11 @@ var SchedulesPG = class SchedulesPG extends SchedulesStorage {
23056
23102
  tableName: TABLE_SCHEDULES,
23057
23103
  schema: TABLE_SCHEMAS[TABLE_SCHEDULES]
23058
23104
  });
23105
+ await this.#db.alterTable({
23106
+ tableName: TABLE_SCHEDULES,
23107
+ schema: TABLE_SCHEMAS[TABLE_SCHEDULES],
23108
+ ifNotExists: ["run_at", "end_at"]
23109
+ });
23059
23110
  await this.#db.createTable({
23060
23111
  tableName: TABLE_SCHEDULE_TRIGGERS,
23061
23112
  schema: TABLE_SCHEMAS[TABLE_SCHEDULE_TRIGGERS]
@@ -23183,7 +23234,9 @@ var SchedulesPG = class SchedulesPG extends SchedulesStorage {
23183
23234
  updated_at: schedule.updatedAt,
23184
23235
  metadata: schedule.metadata ?? null,
23185
23236
  owner_type: schedule.ownerType ?? null,
23186
- owner_id: schedule.ownerId ?? null
23237
+ owner_id: schedule.ownerId ?? null,
23238
+ run_at: schedule.runAt ?? null,
23239
+ end_at: schedule.endAt ?? null
23187
23240
  }
23188
23241
  });
23189
23242
  return schedule;
@@ -23253,6 +23306,8 @@ var SchedulesPG = class SchedulesPG extends SchedulesStorage {
23253
23306
  if ("metadata" in patch) push("metadata = ?::jsonb", patch.metadata != null ? toPgJson(patch.metadata) : null);
23254
23307
  if ("ownerType" in patch) push("owner_type = ?", patch.ownerType ?? null);
23255
23308
  if ("ownerId" in patch) push("owner_id = ?", patch.ownerId ?? null);
23309
+ if ("runAt" in patch) push("run_at = ?", patch.runAt ?? null);
23310
+ if ("endAt" in patch) push("end_at = ?", patch.endAt ?? null);
23256
23311
  push("updated_at = ?", Date.now());
23257
23312
  if (setClauses.length === 1) {
23258
23313
  const existing = await this.#getSchedule(this.#client, id);
@@ -28077,18 +28132,29 @@ var PgFactoryStorageOps = class {
28077
28132
  };
28078
28133
  if (this.#transactionClient) return run(this.#transactionClient);
28079
28134
  const client = await this.#queryable.connect();
28135
+ let releaseError;
28080
28136
  try {
28081
- await client.query("BEGIN");
28137
+ try {
28138
+ await client.query("BEGIN");
28139
+ } catch (error) {
28140
+ releaseError = error instanceof Error ? error : new Error(String(error));
28141
+ throw error;
28142
+ }
28082
28143
  try {
28083
28144
  const result = await run(client);
28084
28145
  await client.query("COMMIT");
28085
28146
  return result;
28086
28147
  } catch (error) {
28087
- await client.query("ROLLBACK");
28148
+ try {
28149
+ await client.query("ROLLBACK");
28150
+ } catch (rollbackError) {
28151
+ releaseError = rollbackError instanceof Error ? rollbackError : new Error(String(rollbackError));
28152
+ throw new AggregateError([error, rollbackError], "Factory transaction and rollback both failed");
28153
+ }
28088
28154
  throw error;
28089
28155
  }
28090
28156
  } finally {
28091
- client.release();
28157
+ client.release(releaseError);
28092
28158
  }
28093
28159
  }
28094
28160
  };
@@ -28145,7 +28211,12 @@ var PgFactoryStorage = class extends FactoryStorage {
28145
28211
  let transactionOpen = false;
28146
28212
  let releaseError;
28147
28213
  try {
28148
- await client.query(options?.isolationLevel === "serializable" ? "BEGIN ISOLATION LEVEL SERIALIZABLE" : "BEGIN");
28214
+ try {
28215
+ await client.query(options?.isolationLevel === "serializable" ? "BEGIN ISOLATION LEVEL SERIALIZABLE" : "BEGIN");
28216
+ } catch (error) {
28217
+ releaseError = error instanceof Error ? error : new Error(String(error));
28218
+ throw error;
28219
+ }
28149
28220
  transactionOpen = true;
28150
28221
  const result = await fn(new PgFactoryStorageOps(client, this.#schemas, client));
28151
28222
  await client.query("COMMIT");
@@ -28347,8 +28418,9 @@ var PostgresStore = class extends MastraCompositeStore {
28347
28418
  this.#ownsWritePool = true;
28348
28419
  }
28349
28420
  this.#readPool = config.readPool ?? this.#writePool;
28350
- this.#db = new RoutingDbClient(new PoolAdapter(this.#writePool));
28351
- this.#readDb = this.#readPool === this.#writePool ? this.#db : new PoolAdapter(this.#readPool);
28421
+ const onRollbackError = (error) => this.logger?.error?.("Transaction rollback failed", { error });
28422
+ this.#db = new RoutingDbClient(new PoolAdapter(this.#writePool, onRollbackError));
28423
+ this.#readDb = this.#readPool === this.#writePool ? this.#db : new PoolAdapter(this.#readPool, onRollbackError);
28352
28424
  const domainConfig = {
28353
28425
  client: this.#db,
28354
28426
  readClient: this.#readDb,
@@ -28417,7 +28489,7 @@ var PostgresStore = class extends MastraCompositeStore {
28417
28489
  let pinned;
28418
28490
  try {
28419
28491
  pinnedClient = await this.#writePool.connect();
28420
- pinned = new PinnedClientAdapter(this.#writePool, pinnedClient);
28492
+ pinned = new PinnedClientAdapter(this.#writePool, pinnedClient, (error) => this.logger?.error?.("Transaction rollback failed", { error }));
28421
28493
  this.#db.pin(pinned);
28422
28494
  for (let attempt = 1; attempt <= 3; attempt++) {
28423
28495
  this.#db.setSchemaSnapshot(await loadSchemaSnapshot(pinned, this.schema));
@@ -28442,7 +28514,7 @@ var PostgresStore = class extends MastraCompositeStore {
28442
28514
  this.#db.setSchemaSnapshot(null);
28443
28515
  if (pinnedClient) {
28444
28516
  this.#db.unpin();
28445
- pinnedClient.release();
28517
+ pinnedClient.release(pinned?.shouldDestroyClient || void 0);
28446
28518
  }
28447
28519
  }
28448
28520
  }