@mastra/pg 1.10.1 → 1.11.0-alpha.0

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
@@ -2038,6 +2038,7 @@ var PoolAdapter = class {
2038
2038
  constructor($pool) {
2039
2039
  this.$pool = $pool;
2040
2040
  }
2041
+ $pool;
2041
2042
  connect() {
2042
2043
  return this.$pool.connect();
2043
2044
  }
@@ -2106,6 +2107,7 @@ var TransactionClient = class {
2106
2107
  constructor(client) {
2107
2108
  this.client = client;
2108
2109
  }
2110
+ client;
2109
2111
  async none(query, values) {
2110
2112
  await this.client.query(query, values);
2111
2113
  return null;
@@ -3543,12 +3545,12 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3543
3545
  await this.#db.alterTable({
3544
3546
  tableName: storage.TABLE_AGENTS,
3545
3547
  schema: storage.TABLE_SCHEMAS[storage.TABLE_AGENTS],
3546
- ifNotExists: ["status", "authorId"]
3548
+ ifNotExists: ["status", "authorId", "visibility", "favoriteCount"]
3547
3549
  });
3548
3550
  await this.#db.alterTable({
3549
3551
  tableName: storage.TABLE_AGENT_VERSIONS,
3550
3552
  schema: storage.TABLE_SCHEMAS[storage.TABLE_AGENT_VERSIONS],
3551
- ifNotExists: ["mcpClients", "requestContextSchema", "workspace", "skills", "skillsFormat"]
3553
+ ifNotExists: ["mcpClients", "requestContextSchema", "workspace", "skills", "skillsFormat", "browser"]
3552
3554
  });
3553
3555
  await this.#migrateToolsToJsonbFormat();
3554
3556
  await this.createDefaultIndexes();
@@ -3689,14 +3691,28 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3689
3691
  }
3690
3692
  }
3691
3693
  /**
3692
- * Removes stale draft agent records that have no activeVersionId.
3694
+ * Removes stale draft agent records that have no versions at all.
3693
3695
  * These are left behind when createAgent partially fails (inserts thin record
3694
3696
  * but fails to create the version due to schema mismatch).
3697
+ *
3698
+ * A legitimate draft (never published) will have rows in the versions table,
3699
+ * so we must only delete records with zero associated versions.
3695
3700
  */
3696
3701
  async #cleanupStaleDrafts() {
3697
3702
  try {
3698
- const fullTableName = getTableName2({ indexName: storage.TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
3699
- await this.#db.client.none(`DELETE FROM ${fullTableName} WHERE status = 'draft' AND "activeVersionId" IS NULL`);
3703
+ const agentsTable = getTableName2({ indexName: storage.TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
3704
+ const versionsTable = getTableName2({
3705
+ indexName: storage.TABLE_AGENT_VERSIONS,
3706
+ schemaName: getSchemaName2(this.#schema)
3707
+ });
3708
+ await this.#db.client.none(
3709
+ `DELETE FROM ${agentsTable} a
3710
+ WHERE a.status = 'draft'
3711
+ AND a."activeVersionId" IS NULL
3712
+ AND NOT EXISTS (
3713
+ SELECT 1 FROM ${versionsTable} v WHERE v."agentId" = a.id
3714
+ )`
3715
+ );
3700
3716
  } catch {
3701
3717
  }
3702
3718
  }
@@ -3725,7 +3741,9 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3725
3741
  status: row.status,
3726
3742
  activeVersionId: row.activeVersionId,
3727
3743
  authorId: row.authorId,
3744
+ visibility: row.visibility ?? void 0,
3728
3745
  metadata: parseJsonResilient(row.metadata),
3746
+ favoriteCount: row.favoriteCount === null || row.favoriteCount === void 0 ? 0 : Number(row.favoriteCount),
3729
3747
  createdAt: row.createdAtZ || row.createdAt,
3730
3748
  updatedAt: row.updatedAtZ || row.updatedAt
3731
3749
  };
@@ -3757,17 +3775,20 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3757
3775
  const agentsTable = getTableName2({ indexName: storage.TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
3758
3776
  const now = /* @__PURE__ */ new Date();
3759
3777
  const nowIso = now.toISOString();
3778
+ const visibility = agent.visibility ?? (agent.authorId ? "private" : null);
3760
3779
  await this.#db.client.none(
3761
3780
  `INSERT INTO ${agentsTable} (
3762
- id, status, "authorId", metadata,
3781
+ id, status, "authorId", visibility, metadata, "favoriteCount",
3763
3782
  "activeVersionId",
3764
3783
  "createdAt", "createdAtZ", "updatedAt", "updatedAtZ"
3765
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
3784
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
3766
3785
  [
3767
3786
  agent.id,
3768
3787
  "draft",
3769
3788
  agent.authorId ?? null,
3789
+ visibility,
3770
3790
  agent.metadata ? JSON.stringify(agent.metadata) : null,
3791
+ 0,
3771
3792
  null,
3772
3793
  // activeVersionId starts as null
3773
3794
  nowIso,
@@ -3776,7 +3797,7 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3776
3797
  nowIso
3777
3798
  ]
3778
3799
  );
3779
- const { id: _id, authorId: _authorId, metadata: _metadata, ...snapshotConfig } = agent;
3800
+ const { id: _id, authorId: _authorId, visibility: _visibility, metadata: _metadata, ...snapshotConfig } = agent;
3780
3801
  const versionId = crypto.randomUUID();
3781
3802
  await this.createVersion({
3782
3803
  id: versionId,
@@ -3791,7 +3812,9 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3791
3812
  status: "draft",
3792
3813
  activeVersionId: void 0,
3793
3814
  authorId: agent.authorId,
3815
+ visibility: visibility ?? void 0,
3794
3816
  metadata: agent.metadata,
3817
+ favoriteCount: 0,
3795
3818
  createdAt: now,
3796
3819
  updatedAt: now
3797
3820
  };
@@ -3830,7 +3853,7 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3830
3853
  details: { agentId: id }
3831
3854
  });
3832
3855
  }
3833
- const { authorId, activeVersionId, metadata, status } = updates;
3856
+ const { authorId, activeVersionId, metadata, status, visibility } = updates;
3834
3857
  const setClauses = [];
3835
3858
  const values = [];
3836
3859
  let paramIndex = 1;
@@ -3846,6 +3869,10 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3846
3869
  setClauses.push(`status = $${paramIndex++}`);
3847
3870
  values.push(status);
3848
3871
  }
3872
+ if (visibility !== void 0) {
3873
+ setClauses.push(`visibility = $${paramIndex++}`);
3874
+ values.push(visibility);
3875
+ }
3849
3876
  if (metadata !== void 0) {
3850
3877
  setClauses.push(`metadata = $${paramIndex++}`);
3851
3878
  values.push(JSON.stringify(metadata));
@@ -3902,7 +3929,18 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3902
3929
  }
3903
3930
  }
3904
3931
  async list(args) {
3905
- const { page = 0, perPage: perPageInput, orderBy, authorId, metadata, status } = args || {};
3932
+ const {
3933
+ page = 0,
3934
+ perPage: perPageInput,
3935
+ orderBy,
3936
+ authorId,
3937
+ metadata,
3938
+ status,
3939
+ visibility,
3940
+ entityIds,
3941
+ pinFavoritedFor,
3942
+ favoritedOnly
3943
+ } = args || {};
3906
3944
  const { field, direction } = this.parseOrderBy(orderBy);
3907
3945
  if (page < 0) {
3908
3946
  throw new error.MastraError(
@@ -3918,26 +3956,58 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3918
3956
  const perPage = storage.normalizePerPage(perPageInput, 100);
3919
3957
  const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
3920
3958
  try {
3959
+ if (entityIds && entityIds.length === 0) {
3960
+ return {
3961
+ agents: [],
3962
+ total: 0,
3963
+ page,
3964
+ perPage: perPageForResponse,
3965
+ hasMore: false
3966
+ };
3967
+ }
3921
3968
  const tableName = getTableName2({ indexName: storage.TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
3969
+ const favoritesTable = getTableName2({ indexName: storage.TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
3922
3970
  const conditions = [];
3923
3971
  const queryParams = [];
3924
3972
  let paramIdx = 1;
3973
+ const joinUserId = pinFavoritedFor;
3974
+ const useJoin = Boolean(joinUserId);
3975
+ let joinSqlIdx = null;
3976
+ if (useJoin) {
3977
+ joinSqlIdx = paramIdx++;
3978
+ }
3925
3979
  if (status) {
3926
- conditions.push(`status = $${paramIdx++}`);
3980
+ conditions.push(`a.status = $${paramIdx++}`);
3927
3981
  queryParams.push(status);
3928
3982
  }
3929
3983
  if (authorId !== void 0) {
3930
- conditions.push(`"authorId" = $${paramIdx++}`);
3984
+ conditions.push(`a."authorId" = $${paramIdx++}`);
3931
3985
  queryParams.push(authorId);
3932
3986
  }
3987
+ if (visibility !== void 0) {
3988
+ conditions.push(`a.visibility = $${paramIdx++}`);
3989
+ queryParams.push(visibility);
3990
+ }
3933
3991
  if (metadata && Object.keys(metadata).length > 0) {
3934
- conditions.push(`metadata @> $${paramIdx++}::jsonb`);
3992
+ conditions.push(`a.metadata @> $${paramIdx++}::jsonb`);
3935
3993
  queryParams.push(JSON.stringify(metadata));
3936
3994
  }
3995
+ if (entityIds && entityIds.length > 0) {
3996
+ const placeholders = entityIds.map(() => `$${paramIdx++}`).join(", ");
3997
+ conditions.push(`a.id IN (${placeholders})`);
3998
+ queryParams.push(...entityIds);
3999
+ }
4000
+ if (useJoin && favoritedOnly) {
4001
+ conditions.push('s."userId" IS NOT NULL');
4002
+ } else if (favoritedOnly) {
4003
+ conditions.push("1=0");
4004
+ }
4005
+ const joinClause = useJoin && joinSqlIdx !== null ? `LEFT JOIN ${favoritesTable} s ON s."entityType" = 'agent' AND s."entityId" = a.id AND s."userId" = $${joinSqlIdx}` : "";
4006
+ const joinParams = useJoin && joinUserId ? [joinUserId] : [];
3937
4007
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
3938
4008
  const countResult = await this.#db.client.one(
3939
- `SELECT COUNT(*) as count FROM ${tableName} ${whereClause}`,
3940
- queryParams
4009
+ `SELECT COUNT(*) as count FROM ${tableName} a ${joinClause} ${whereClause}`,
4010
+ [...joinParams, ...queryParams]
3941
4011
  );
3942
4012
  const total = parseInt(countResult.count, 10);
3943
4013
  if (total === 0) {
@@ -3949,10 +4019,19 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3949
4019
  hasMore: false
3950
4020
  };
3951
4021
  }
4022
+ const orderByParts = [];
4023
+ if (useJoin) {
4024
+ orderByParts.push(`(s."userId" IS NOT NULL) DESC`);
4025
+ }
4026
+ orderByParts.push(`a."${field}" ${direction}`);
4027
+ orderByParts.push(`a."id" ASC`);
4028
+ const orderByClause = `ORDER BY ${orderByParts.join(", ")}`;
3952
4029
  const limitValue = perPageInput === false ? total : perPage;
4030
+ const limitIdx = paramIdx++;
4031
+ const offsetIdx = paramIdx++;
3953
4032
  const dataResult = await this.#db.client.manyOrNone(
3954
- `SELECT * FROM ${tableName} ${whereClause} ORDER BY "${field}" ${direction} LIMIT $${paramIdx++} OFFSET $${paramIdx++}`,
3955
- [...queryParams, limitValue, offset]
4033
+ `SELECT a.* FROM ${tableName} a ${joinClause} ${whereClause} ${orderByClause} LIMIT $${limitIdx} OFFSET $${offsetIdx}`,
4034
+ [...joinParams, ...queryParams, limitValue, offset]
3956
4035
  );
3957
4036
  const agents = (dataResult || []).flatMap((row) => {
3958
4037
  try {
@@ -3996,9 +4075,10 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
3996
4075
  "defaultOptions", workflows, agents, "integrationTools",
3997
4076
  "inputProcessors", "outputProcessors", memory, scorers,
3998
4077
  "mcpClients", "requestContextSchema", workspace, skills, "skillsFormat",
4078
+ browser,
3999
4079
  "changedFields", "changeMessage",
4000
4080
  "createdAt", "createdAtZ"
4001
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25)`,
4081
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26)`,
4002
4082
  [
4003
4083
  input.id,
4004
4084
  input.agentId,
@@ -4021,6 +4101,7 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
4021
4101
  input.workspace ? JSON.stringify(input.workspace) : null,
4022
4102
  input.skills ? JSON.stringify(input.skills) : null,
4023
4103
  input.skillsFormat ?? null,
4104
+ input.browser ? JSON.stringify(input.browser) : null,
4024
4105
  input.changedFields ? JSON.stringify(input.changedFields) : null,
4025
4106
  input.changeMessage ?? null,
4026
4107
  nowIso,
@@ -4270,6 +4351,7 @@ var AgentsPG = class _AgentsPG extends storage.AgentsStorage {
4270
4351
  workspace: parseJsonResilient(row.workspace),
4271
4352
  skills: parseJsonResilient(row.skills),
4272
4353
  skillsFormat: row.skillsFormat,
4354
+ browser: parseJsonResilient(row.browser),
4273
4355
  changedFields: parseJsonResilient(row.changedFields),
4274
4356
  changeMessage: row.changeMessage,
4275
4357
  createdAt: row.createdAtZ || row.createdAt
@@ -6421,6 +6503,250 @@ var ExperimentsPG = class _ExperimentsPG extends storage.ExperimentsStorage {
6421
6503
  await this.#db.clearTable({ tableName: storage.TABLE_EXPERIMENTS });
6422
6504
  }
6423
6505
  };
6506
+ var ENTITY_TABLE = {
6507
+ agent: storage.TABLE_AGENTS,
6508
+ skill: storage.TABLE_SKILLS
6509
+ };
6510
+ var FavoritesPG = class _FavoritesPG extends storage.FavoritesStorage {
6511
+ #db;
6512
+ #schema;
6513
+ static MANAGED_TABLES = [storage.TABLE_FAVORITES];
6514
+ constructor(config) {
6515
+ super();
6516
+ const { client, schemaName, skipDefaultIndexes } = resolvePgConfig(config);
6517
+ this.#db = new PgDB({ client, schemaName, skipDefaultIndexes });
6518
+ this.#schema = schemaName || "public";
6519
+ }
6520
+ static getExportDDL(schemaName) {
6521
+ const statements = [];
6522
+ for (const tableName of _FavoritesPG.MANAGED_TABLES) {
6523
+ statements.push(
6524
+ generateTableSQL({
6525
+ tableName,
6526
+ schema: storage.TABLE_SCHEMAS[tableName],
6527
+ schemaName,
6528
+ compositePrimaryKey: ["userId", "entityType", "entityId"],
6529
+ includeAllConstraints: true
6530
+ })
6531
+ );
6532
+ }
6533
+ const fullFavoritesTable = getTableName2({ indexName: storage.TABLE_FAVORITES, schemaName: getSchemaName2(schemaName) });
6534
+ statements.push(
6535
+ `CREATE INDEX IF NOT EXISTS idx_favorites_entity ON ${fullFavoritesTable} ("entityType", "entityId")`
6536
+ );
6537
+ return statements;
6538
+ }
6539
+ async init() {
6540
+ await this.#db.createTable({
6541
+ tableName: storage.TABLE_FAVORITES,
6542
+ schema: storage.TABLE_SCHEMAS[storage.TABLE_FAVORITES],
6543
+ compositePrimaryKey: ["userId", "entityType", "entityId"]
6544
+ });
6545
+ const fullTableName = getTableName2({ indexName: storage.TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6546
+ await this.#db.client.none(
6547
+ `CREATE INDEX IF NOT EXISTS idx_favorites_entity ON ${fullTableName} ("entityType", "entityId")`
6548
+ );
6549
+ }
6550
+ async dangerouslyClearAll() {
6551
+ const fullTableName = getTableName2({ indexName: storage.TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6552
+ const fullAgentsTable = getTableName2({ indexName: storage.TABLE_AGENTS, schemaName: getSchemaName2(this.#schema) });
6553
+ const fullSkillsTable = getTableName2({ indexName: storage.TABLE_SKILLS, schemaName: getSchemaName2(this.#schema) });
6554
+ await this.#db.client.tx(async (t) => {
6555
+ await t.none(`DELETE FROM ${fullTableName}`);
6556
+ await t.none(`UPDATE ${fullAgentsTable} SET "favoriteCount" = 0 WHERE "favoriteCount" > 0`);
6557
+ await t.none(`UPDATE ${fullSkillsTable} SET "favoriteCount" = 0 WHERE "favoriteCount" > 0`);
6558
+ });
6559
+ }
6560
+ async favorite(input) {
6561
+ const { userId, entityType, entityId } = input;
6562
+ const entityTable = ENTITY_TABLE[entityType];
6563
+ const fullFavoritesTable = getTableName2({ indexName: storage.TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6564
+ const fullEntityTable = getTableName2({ indexName: entityTable, schemaName: getSchemaName2(this.#schema) });
6565
+ try {
6566
+ return await this.#db.client.tx(async (t) => {
6567
+ const entityRow = await t.oneOrNone(`SELECT "favoriteCount" FROM ${fullEntityTable} WHERE id = $1`, [entityId]);
6568
+ if (!entityRow) {
6569
+ throw new error.MastraError({
6570
+ id: storage.createStorageErrorId("PG", "FAVORITE", "ENTITY_NOT_FOUND"),
6571
+ domain: error.ErrorDomain.STORAGE,
6572
+ category: error.ErrorCategory.USER,
6573
+ text: `${entityType} ${entityId} not found`,
6574
+ details: { entityType, entityId }
6575
+ });
6576
+ }
6577
+ const inserted = await t.oneOrNone(
6578
+ `INSERT INTO ${fullFavoritesTable} ("userId", "entityType", "entityId", "createdAt", "createdAtZ")
6579
+ VALUES ($1, $2, $3, $4, $5)
6580
+ ON CONFLICT ("userId", "entityType", "entityId") DO NOTHING
6581
+ RETURNING "userId"`,
6582
+ [userId, entityType, entityId, (/* @__PURE__ */ new Date()).toISOString(), (/* @__PURE__ */ new Date()).toISOString()]
6583
+ );
6584
+ if (inserted) {
6585
+ await t.none(
6586
+ `UPDATE ${fullEntityTable} SET "favoriteCount" = COALESCE("favoriteCount", 0) + 1 WHERE id = $1`,
6587
+ [entityId]
6588
+ );
6589
+ }
6590
+ const after = await t.one(`SELECT "favoriteCount" FROM ${fullEntityTable} WHERE id = $1`, [entityId]);
6591
+ const favoriteCount = Number(after.favoriteCount ?? 0);
6592
+ return { favorited: true, favoriteCount };
6593
+ });
6594
+ } catch (error$1) {
6595
+ if (error$1 instanceof error.MastraError) throw error$1;
6596
+ throw new error.MastraError(
6597
+ {
6598
+ id: storage.createStorageErrorId("PG", "FAVORITE", "FAILED"),
6599
+ domain: error.ErrorDomain.STORAGE,
6600
+ category: error.ErrorCategory.THIRD_PARTY,
6601
+ details: { entityType, entityId }
6602
+ },
6603
+ error$1
6604
+ );
6605
+ }
6606
+ }
6607
+ async unfavorite(input) {
6608
+ const { userId, entityType, entityId } = input;
6609
+ const entityTable = ENTITY_TABLE[entityType];
6610
+ const fullFavoritesTable = getTableName2({ indexName: storage.TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6611
+ const fullEntityTable = getTableName2({ indexName: entityTable, schemaName: getSchemaName2(this.#schema) });
6612
+ try {
6613
+ return await this.#db.client.tx(async (t) => {
6614
+ const entityRow = await t.oneOrNone(`SELECT "favoriteCount" FROM ${fullEntityTable} WHERE id = $1`, [entityId]);
6615
+ if (!entityRow) {
6616
+ throw new error.MastraError({
6617
+ id: storage.createStorageErrorId("PG", "UNFAVORITE", "ENTITY_NOT_FOUND"),
6618
+ domain: error.ErrorDomain.STORAGE,
6619
+ category: error.ErrorCategory.USER,
6620
+ text: `${entityType} ${entityId} not found`,
6621
+ details: { entityType, entityId }
6622
+ });
6623
+ }
6624
+ const deleted = await t.oneOrNone(
6625
+ `DELETE FROM ${fullFavoritesTable} WHERE "userId" = $1 AND "entityType" = $2 AND "entityId" = $3 RETURNING "userId"`,
6626
+ [userId, entityType, entityId]
6627
+ );
6628
+ if (deleted) {
6629
+ await t.none(
6630
+ `UPDATE ${fullEntityTable} SET "favoriteCount" = GREATEST(COALESCE("favoriteCount", 0) - 1, 0) WHERE id = $1`,
6631
+ [entityId]
6632
+ );
6633
+ }
6634
+ const after = await t.one(`SELECT "favoriteCount" FROM ${fullEntityTable} WHERE id = $1`, [entityId]);
6635
+ const favoriteCount = Number(after.favoriteCount ?? 0);
6636
+ return { favorited: false, favoriteCount };
6637
+ });
6638
+ } catch (error$1) {
6639
+ if (error$1 instanceof error.MastraError) throw error$1;
6640
+ throw new error.MastraError(
6641
+ {
6642
+ id: storage.createStorageErrorId("PG", "UNFAVORITE", "FAILED"),
6643
+ domain: error.ErrorDomain.STORAGE,
6644
+ category: error.ErrorCategory.THIRD_PARTY,
6645
+ details: { entityType, entityId }
6646
+ },
6647
+ error$1
6648
+ );
6649
+ }
6650
+ }
6651
+ async isFavorited(input) {
6652
+ const fullFavoritesTable = getTableName2({ indexName: storage.TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6653
+ try {
6654
+ const result = await this.#db.client.oneOrNone(
6655
+ `SELECT 1 FROM ${fullFavoritesTable} WHERE "userId" = $1 AND "entityType" = $2 AND "entityId" = $3 LIMIT 1`,
6656
+ [input.userId, input.entityType, input.entityId]
6657
+ );
6658
+ return result !== null;
6659
+ } catch (error$1) {
6660
+ if (error$1 instanceof error.MastraError) throw error$1;
6661
+ throw new error.MastraError(
6662
+ {
6663
+ id: storage.createStorageErrorId("PG", "IS_FAVORITED", "FAILED"),
6664
+ domain: error.ErrorDomain.STORAGE,
6665
+ category: error.ErrorCategory.THIRD_PARTY
6666
+ },
6667
+ error$1
6668
+ );
6669
+ }
6670
+ }
6671
+ async isFavoritedBatch(input) {
6672
+ const { userId, entityType, entityIds } = input;
6673
+ if (entityIds.length === 0) {
6674
+ return /* @__PURE__ */ new Set();
6675
+ }
6676
+ const fullFavoritesTable = getTableName2({ indexName: storage.TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6677
+ try {
6678
+ const placeholders = entityIds.map((_, i) => `$${i + 3}`).join(", ");
6679
+ const rows = await this.#db.client.manyOrNone(
6680
+ `SELECT "entityId" FROM ${fullFavoritesTable} WHERE "userId" = $1 AND "entityType" = $2 AND "entityId" IN (${placeholders})`,
6681
+ [userId, entityType, ...entityIds]
6682
+ );
6683
+ const set = /* @__PURE__ */ new Set();
6684
+ for (const row of rows ?? []) {
6685
+ set.add(row.entityId);
6686
+ }
6687
+ return set;
6688
+ } catch (error$1) {
6689
+ if (error$1 instanceof error.MastraError) throw error$1;
6690
+ throw new error.MastraError(
6691
+ {
6692
+ id: storage.createStorageErrorId("PG", "IS_FAVORITED_BATCH", "FAILED"),
6693
+ domain: error.ErrorDomain.STORAGE,
6694
+ category: error.ErrorCategory.THIRD_PARTY
6695
+ },
6696
+ error$1
6697
+ );
6698
+ }
6699
+ }
6700
+ async listFavoritedIds(input) {
6701
+ const fullFavoritesTable = getTableName2({ indexName: storage.TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6702
+ try {
6703
+ const rows = await this.#db.client.manyOrNone(
6704
+ `SELECT "entityId" FROM ${fullFavoritesTable} WHERE "userId" = $1 AND "entityType" = $2 ORDER BY "createdAt" DESC, "entityId" ASC`,
6705
+ [input.userId, input.entityType]
6706
+ );
6707
+ return (rows ?? []).map((row) => row.entityId);
6708
+ } catch (error$1) {
6709
+ if (error$1 instanceof error.MastraError) throw error$1;
6710
+ throw new error.MastraError(
6711
+ {
6712
+ id: storage.createStorageErrorId("PG", "LIST_FAVORITED_IDS", "FAILED"),
6713
+ domain: error.ErrorDomain.STORAGE,
6714
+ category: error.ErrorCategory.THIRD_PARTY
6715
+ },
6716
+ error$1
6717
+ );
6718
+ }
6719
+ }
6720
+ async deleteFavoritesForEntity(input) {
6721
+ const fullFavoritesTable = getTableName2({ indexName: storage.TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
6722
+ const entityTable = ENTITY_TABLE[input.entityType];
6723
+ const fullEntityTable = getTableName2({ indexName: entityTable, schemaName: getSchemaName2(this.#schema) });
6724
+ try {
6725
+ return await this.#db.client.tx(async (t) => {
6726
+ const result = await t.one(
6727
+ `WITH deleted AS (
6728
+ DELETE FROM ${fullFavoritesTable} WHERE "entityType" = $1 AND "entityId" = $2 RETURNING 1
6729
+ )
6730
+ SELECT COUNT(*)::text AS count FROM deleted`,
6731
+ [input.entityType, input.entityId]
6732
+ );
6733
+ await t.none(`UPDATE ${fullEntityTable} SET "favoriteCount" = 0 WHERE id = $1`, [input.entityId]);
6734
+ return Number(result.count);
6735
+ });
6736
+ } catch (error$1) {
6737
+ if (error$1 instanceof error.MastraError) throw error$1;
6738
+ throw new error.MastraError(
6739
+ {
6740
+ id: storage.createStorageErrorId("PG", "DELETE_FAVORITES_FOR_ENTITY", "FAILED"),
6741
+ domain: error.ErrorDomain.STORAGE,
6742
+ category: error.ErrorCategory.THIRD_PARTY,
6743
+ details: { entityType: input.entityType, entityId: input.entityId }
6744
+ },
6745
+ error$1
6746
+ );
6747
+ }
6748
+ }
6749
+ };
6424
6750
  var SNAPSHOT_FIELDS = ["name", "description", "servers"];
6425
6751
  var MCPClientsPG = class _MCPClientsPG extends storage.MCPClientsStorage {
6426
6752
  #db;
@@ -12727,6 +13053,7 @@ var SNAPSHOT_FIELDS5 = [
12727
13053
  "references",
12728
13054
  "scripts",
12729
13055
  "assets",
13056
+ "files",
12730
13057
  "metadata",
12731
13058
  "tree"
12732
13059
  ];
@@ -12774,6 +13101,16 @@ var SkillsPG = class _SkillsPG extends storage.SkillsStorage {
12774
13101
  tableName: storage.TABLE_SKILL_VERSIONS,
12775
13102
  schema: storage.TABLE_SCHEMAS[storage.TABLE_SKILL_VERSIONS]
12776
13103
  });
13104
+ await this.#db.alterTable({
13105
+ tableName: storage.TABLE_SKILLS,
13106
+ schema: storage.TABLE_SCHEMAS[storage.TABLE_SKILLS],
13107
+ ifNotExists: ["visibility", "favoriteCount"]
13108
+ });
13109
+ await this.#db.alterTable({
13110
+ tableName: storage.TABLE_SKILL_VERSIONS,
13111
+ schema: storage.TABLE_SCHEMAS[storage.TABLE_SKILL_VERSIONS],
13112
+ ifNotExists: ["files"]
13113
+ });
12777
13114
  await this.createDefaultIndexes();
12778
13115
  await this.createCustomIndexes();
12779
13116
  }
@@ -12823,14 +13160,15 @@ var SkillsPG = class _SkillsPG extends storage.SkillsStorage {
12823
13160
  const tableName = getTableName2({ indexName: storage.TABLE_SKILLS, schemaName: getSchemaName2(this.#schema) });
12824
13161
  const now = /* @__PURE__ */ new Date();
12825
13162
  const nowIso = now.toISOString();
13163
+ const visibility = skill.visibility ?? (skill.authorId ? "private" : void 0);
12826
13164
  await this.#db.client.none(
12827
13165
  `INSERT INTO ${tableName} (
12828
- id, status, "activeVersionId", "authorId",
13166
+ id, status, "activeVersionId", "authorId", visibility, "favoriteCount",
12829
13167
  "createdAt", "createdAtZ", "updatedAt", "updatedAtZ"
12830
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
12831
- [skill.id, "draft", null, skill.authorId ?? null, nowIso, nowIso, nowIso, nowIso]
13168
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`,
13169
+ [skill.id, "draft", null, skill.authorId ?? null, visibility ?? null, 0, nowIso, nowIso, nowIso, nowIso]
12832
13170
  );
12833
- const { id: _id, authorId: _authorId, ...snapshotConfig } = skill;
13171
+ const { id: _id, authorId: _authorId, visibility: _visibility, ...snapshotConfig } = skill;
12834
13172
  const versionId = crypto.randomUUID();
12835
13173
  await this.createVersion({
12836
13174
  id: versionId,
@@ -12845,6 +13183,8 @@ var SkillsPG = class _SkillsPG extends storage.SkillsStorage {
12845
13183
  status: "draft",
12846
13184
  activeVersionId: void 0,
12847
13185
  authorId: skill.authorId,
13186
+ visibility,
13187
+ favoriteCount: 0,
12848
13188
  createdAt: now,
12849
13189
  updatedAt: now
12850
13190
  };
@@ -12886,8 +13226,12 @@ var SkillsPG = class _SkillsPG extends storage.SkillsStorage {
12886
13226
  details: { skillId: id }
12887
13227
  });
12888
13228
  }
12889
- const { authorId, activeVersionId, status, ...configFields } = updates;
13229
+ const { authorId, visibility, activeVersionId, status, ...rawConfigFields } = updates;
12890
13230
  let versionCreated = false;
13231
+ const configFields = {};
13232
+ for (const [key, value] of Object.entries(rawConfigFields)) {
13233
+ if (value !== void 0) configFields[key] = value;
13234
+ }
12891
13235
  const hasConfigUpdate = SNAPSHOT_FIELDS5.some((field) => field in configFields);
12892
13236
  if (hasConfigUpdate) {
12893
13237
  const latestVersion = await this.getLatestVersion(id);
@@ -12933,6 +13277,10 @@ var SkillsPG = class _SkillsPG extends storage.SkillsStorage {
12933
13277
  setClauses.push(`"authorId" = $${paramIndex++}`);
12934
13278
  values.push(authorId);
12935
13279
  }
13280
+ if (visibility !== void 0) {
13281
+ setClauses.push(`visibility = $${paramIndex++}`);
13282
+ values.push(visibility);
13283
+ }
12936
13284
  if (activeVersionId !== void 0) {
12937
13285
  setClauses.push(`"activeVersionId" = $${paramIndex++}`);
12938
13286
  values.push(activeVersionId);
@@ -13000,7 +13348,17 @@ var SkillsPG = class _SkillsPG extends storage.SkillsStorage {
13000
13348
  }
13001
13349
  }
13002
13350
  async list(args) {
13003
- const { page = 0, perPage: perPageInput, orderBy, authorId } = args || {};
13351
+ const {
13352
+ page = 0,
13353
+ perPage: perPageInput,
13354
+ orderBy,
13355
+ authorId,
13356
+ visibility,
13357
+ status,
13358
+ entityIds,
13359
+ pinFavoritedFor,
13360
+ favoritedOnly
13361
+ } = args || {};
13004
13362
  const { field, direction } = this.parseOrderBy(orderBy);
13005
13363
  if (page < 0) {
13006
13364
  throw new error.MastraError(
@@ -13016,18 +13374,54 @@ var SkillsPG = class _SkillsPG extends storage.SkillsStorage {
13016
13374
  const perPage = storage.normalizePerPage(perPageInput, 100);
13017
13375
  const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
13018
13376
  try {
13377
+ if (entityIds && entityIds.length === 0) {
13378
+ return {
13379
+ skills: [],
13380
+ total: 0,
13381
+ page,
13382
+ perPage: perPageForResponse,
13383
+ hasMore: false
13384
+ };
13385
+ }
13019
13386
  const tableName = getTableName2({ indexName: storage.TABLE_SKILLS, schemaName: getSchemaName2(this.#schema) });
13387
+ const favoritesTable = getTableName2({ indexName: storage.TABLE_FAVORITES, schemaName: getSchemaName2(this.#schema) });
13020
13388
  const conditions = [];
13021
13389
  const queryParams = [];
13022
13390
  let paramIdx = 1;
13391
+ const joinUserId = pinFavoritedFor;
13392
+ const useJoin = Boolean(joinUserId);
13393
+ let joinSqlIdx = null;
13394
+ if (useJoin) {
13395
+ joinSqlIdx = paramIdx++;
13396
+ }
13397
+ if (status) {
13398
+ conditions.push(`s.status = $${paramIdx++}`);
13399
+ queryParams.push(status);
13400
+ }
13023
13401
  if (authorId !== void 0) {
13024
- conditions.push(`"authorId" = $${paramIdx++}`);
13402
+ conditions.push(`s."authorId" = $${paramIdx++}`);
13025
13403
  queryParams.push(authorId);
13026
13404
  }
13405
+ if (visibility !== void 0) {
13406
+ conditions.push(`s.visibility = $${paramIdx++}`);
13407
+ queryParams.push(visibility);
13408
+ }
13409
+ if (entityIds && entityIds.length > 0) {
13410
+ const placeholders = entityIds.map(() => `$${paramIdx++}`).join(", ");
13411
+ conditions.push(`s.id IN (${placeholders})`);
13412
+ queryParams.push(...entityIds);
13413
+ }
13414
+ if (useJoin && favoritedOnly) {
13415
+ conditions.push('sr."userId" IS NOT NULL');
13416
+ } else if (favoritedOnly) {
13417
+ conditions.push("1=0");
13418
+ }
13419
+ const joinClause = useJoin && joinSqlIdx !== null ? `LEFT JOIN ${favoritesTable} sr ON sr."entityType" = 'skill' AND sr."entityId" = s.id AND sr."userId" = $${joinSqlIdx}` : "";
13420
+ const joinParams = useJoin && joinUserId ? [joinUserId] : [];
13027
13421
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
13028
13422
  const countResult = await this.#db.client.one(
13029
- `SELECT COUNT(*) as count FROM ${tableName} ${whereClause}`,
13030
- queryParams
13423
+ `SELECT COUNT(*) as count FROM ${tableName} s ${joinClause} ${whereClause}`,
13424
+ [...joinParams, ...queryParams]
13031
13425
  );
13032
13426
  const total = parseInt(countResult.count, 10);
13033
13427
  if (total === 0) {
@@ -13039,10 +13433,19 @@ var SkillsPG = class _SkillsPG extends storage.SkillsStorage {
13039
13433
  hasMore: false
13040
13434
  };
13041
13435
  }
13436
+ const orderByParts = [];
13437
+ if (useJoin) {
13438
+ orderByParts.push(`(sr."userId" IS NOT NULL) DESC`);
13439
+ }
13440
+ orderByParts.push(`s."${field}" ${direction}`);
13441
+ orderByParts.push(`s."id" ASC`);
13442
+ const orderByClause = `ORDER BY ${orderByParts.join(", ")}`;
13042
13443
  const limitValue = perPageInput === false ? total : perPage;
13444
+ const limitIdx = paramIdx++;
13445
+ const offsetIdx = paramIdx++;
13043
13446
  const dataResult = await this.#db.client.manyOrNone(
13044
- `SELECT * FROM ${tableName} ${whereClause} ORDER BY "${field}" ${direction} LIMIT $${paramIdx++} OFFSET $${paramIdx++}`,
13045
- [...queryParams, limitValue, offset]
13447
+ `SELECT s.* FROM ${tableName} s ${joinClause} ${whereClause} ${orderByClause} LIMIT $${limitIdx} OFFSET $${offsetIdx}`,
13448
+ [...joinParams, ...queryParams, limitValue, offset]
13046
13449
  );
13047
13450
  const skills = (dataResult || []).flatMap((row) => {
13048
13451
  try {
@@ -13086,10 +13489,10 @@ var SkillsPG = class _SkillsPG extends storage.SkillsStorage {
13086
13489
  `INSERT INTO ${tableName} (
13087
13490
  id, "skillId", "versionNumber",
13088
13491
  name, description, instructions, license, compatibility,
13089
- source, "references", scripts, assets, metadata, tree,
13492
+ source, "references", scripts, assets, files, metadata, tree,
13090
13493
  "changedFields", "changeMessage",
13091
13494
  "createdAt", "createdAtZ"
13092
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)`,
13495
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)`,
13093
13496
  [
13094
13497
  input.id,
13095
13498
  input.skillId,
@@ -13103,6 +13506,7 @@ var SkillsPG = class _SkillsPG extends storage.SkillsStorage {
13103
13506
  input.references ? JSON.stringify(input.references) : null,
13104
13507
  input.scripts ? JSON.stringify(input.scripts) : null,
13105
13508
  input.assets ? JSON.stringify(input.assets) : null,
13509
+ input.files ? JSON.stringify(input.files) : null,
13106
13510
  input.metadata ? JSON.stringify(input.metadata) : null,
13107
13511
  input.tree ? JSON.stringify(input.tree) : null,
13108
13512
  input.changedFields ? JSON.stringify(input.changedFields) : null,
@@ -13345,6 +13749,8 @@ var SkillsPG = class _SkillsPG extends storage.SkillsStorage {
13345
13749
  status: row.status,
13346
13750
  activeVersionId: row.activeVersionId,
13347
13751
  authorId: row.authorId,
13752
+ visibility: row.visibility,
13753
+ favoriteCount: row.favoriteCount === null || row.favoriteCount === void 0 ? 0 : Number(row.favoriteCount),
13348
13754
  createdAt: new Date(row.createdAtZ || row.createdAt),
13349
13755
  updatedAt: new Date(row.updatedAtZ || row.updatedAt)
13350
13756
  };
@@ -13363,6 +13769,7 @@ var SkillsPG = class _SkillsPG extends storage.SkillsStorage {
13363
13769
  references: parseJsonResilient(row.references),
13364
13770
  scripts: parseJsonResilient(row.scripts),
13365
13771
  assets: parseJsonResilient(row.assets),
13772
+ files: parseJsonResilient(row.files),
13366
13773
  metadata: parseJsonResilient(row.metadata),
13367
13774
  tree: parseJsonResilient(row.tree),
13368
13775
  changedFields: parseJsonResilient(row.changedFields),
@@ -13964,8 +14371,12 @@ var WorkspacesPG = class _WorkspacesPG extends storage.WorkspacesStorage {
13964
14371
  details: { workspaceId: id }
13965
14372
  });
13966
14373
  }
13967
- const { authorId, activeVersionId, metadata, status, ...configFields } = updates;
14374
+ const { authorId, activeVersionId, metadata, status, ...rawConfigFields } = updates;
13968
14375
  let versionCreated = false;
14376
+ const configFields = {};
14377
+ for (const [key, value] of Object.entries(rawConfigFields)) {
14378
+ if (value !== void 0) configFields[key] = value;
14379
+ }
13969
14380
  const hasConfigUpdate = SNAPSHOT_FIELDS6.some((field) => field in configFields);
13970
14381
  if (hasConfigUpdate) {
13971
14382
  const latestVersion = await this.getLatestVersion(id);
@@ -14473,6 +14884,7 @@ var ALL_DOMAINS = [
14473
14884
  DatasetsPG,
14474
14885
  ExperimentsPG,
14475
14886
  BackgroundTasksPG,
14887
+ FavoritesPG,
14476
14888
  ChannelsPG,
14477
14889
  SchedulesPG
14478
14890
  ];
@@ -14526,6 +14938,7 @@ var PostgresStore = class extends storage.MastraCompositeStore {
14526
14938
  mcpServers: new MCPServersPG(domainConfig),
14527
14939
  workspaces: new WorkspacesPG(domainConfig),
14528
14940
  skills: new SkillsPG(domainConfig),
14941
+ favorites: new FavoritesPG(domainConfig),
14529
14942
  blobs: new BlobsPG(domainConfig),
14530
14943
  datasets: new DatasetsPG(domainConfig),
14531
14944
  experiments: new ExperimentsPG(domainConfig),
@@ -14726,6 +15139,7 @@ exports.BlobsPG = BlobsPG;
14726
15139
  exports.ChannelsPG = ChannelsPG;
14727
15140
  exports.DatasetsPG = DatasetsPG;
14728
15141
  exports.ExperimentsPG = ExperimentsPG;
15142
+ exports.FavoritesPG = FavoritesPG;
14729
15143
  exports.MCPClientsPG = MCPClientsPG;
14730
15144
  exports.MCPServersPG = MCPServersPG;
14731
15145
  exports.MemoryPG = MemoryPG;