{"version":3,"file":"inmemory-D6UzQ68e.cjs","names":["MastraBase"],"sources":["../src/storage/domains/favorites/base.ts","../src/storage/domains/favorites/inmemory.ts"],"sourcesContent":["import { MastraBase } from '../../../base';\nimport type {\n  StorageDeleteFavoritesForEntityInput,\n  StorageIsFavoritedBatchInput,\n  StorageListFavoritesInput,\n  StorageFavoriteEntityType,\n  StorageFavoriteKey,\n} from '../../types';\n\n/**\n * Result of a favorite/unfavorite operation. `favorited` reflects the new state\n * for the caller; `favoriteCount` reflects the entity's denormalized counter\n * after the operation.\n */\nexport interface FavoriteToggleResult {\n  favorited: boolean;\n  favoriteCount: number;\n}\n\n/**\n * Abstract base class for favorites storage.\n *\n * The favorites domain is responsible for:\n *   - persisting `(userId, entityType, entityId)` favorite rows,\n *   - maintaining the denormalized `favoriteCount` on the parent entity record,\n *   - answering batched lookups for list-response annotation.\n *\n * EE feature gating is the server-handler concern, not the storage domain.\n */\nexport abstract class FavoritesStorage extends MastraBase {\n  constructor() {\n    super({\n      component: 'STORAGE',\n      name: 'FAVORITES',\n    });\n  }\n\n  /**\n   * Initialize the favorites store (create tables, indexes, etc).\n   */\n  abstract init(): Promise<void>;\n\n  /**\n   * Favorite an entity for a user. Idempotent — re-favoriting an already-favorited\n   * entity is a no-op and returns the current state.\n   *\n   * Implementations must atomically insert the favorite row and increment the\n   * entity's `favoriteCount`. If the entity does not exist, throw.\n   */\n  abstract favorite(input: StorageFavoriteKey): Promise<FavoriteToggleResult>;\n\n  /**\n   * Unfavorite an entity for a user. Idempotent — unfavoriting a non-favorited\n   * entity is a no-op and returns the current state.\n   *\n   * Implementations must atomically delete the favorite row and decrement the\n   * entity's `favoriteCount` (clamped at 0). If the entity does not exist,\n   * throw.\n   */\n  abstract unfavorite(input: StorageFavoriteKey): Promise<FavoriteToggleResult>;\n\n  /**\n   * Check whether a single entity is favorited by the given user.\n   */\n  abstract isFavorited(input: StorageFavoriteKey): Promise<boolean>;\n\n  /**\n   * Look up which entity IDs in a candidate set are favorited by the given user.\n   * Used to annotate list responses.\n   *\n   * Returns a Set of favorited entity IDs. Order does not matter.\n   */\n  abstract isFavoritedBatch(input: StorageIsFavoritedBatchInput): Promise<Set<string>>;\n\n  /**\n   * List all entity IDs of the given type favorited by the user.\n   * Used internally by the `?favoritedOnly=true` query handler to pre-filter\n   * the candidate set for the existing list path.\n   */\n  abstract listFavoritedIds(input: StorageListFavoritesInput): Promise<string[]>;\n\n  /**\n   * Remove all favorite rows referencing the given entity. Called by\n   * hard-delete handlers. Decrements no counters (the entity is being\n   * removed).\n   *\n   * Returns the number of favorite rows removed.\n   */\n  abstract deleteFavoritesForEntity(input: StorageDeleteFavoritesForEntityInput): Promise<number>;\n\n  /**\n   * Delete all favorites. Used for testing.\n   */\n  abstract dangerouslyClearAll(): Promise<void>;\n}\n\nexport type { StorageFavoriteEntityType };\n","import type {\n  StorageDeleteFavoritesForEntityInput,\n  StorageIsFavoritedBatchInput,\n  StorageListFavoritesInput,\n  StorageFavoriteEntityType,\n  StorageFavoriteKey,\n  StorageFavoriteType,\n} from '../../types';\nimport type { InMemoryDB } from '../inmemory-db';\nimport type { FavoriteToggleResult } from './base';\nimport { FavoritesStorage } from './base';\n\n/**\n * Build the composite key used by the in-memory favorites Map.\n */\nfunction favoriteKey(userId: string, entityType: StorageFavoriteEntityType, entityId: string): string {\n  return `${userId}\\u0000${entityType}\\u0000${entityId}`;\n}\n\n/**\n * In-memory implementation of FavoritesStorage. Mutates the shared InMemoryDB\n * Maps for favorites and the parent entity records (agents, skills) so that the\n * denormalized `favoriteCount` stays in sync.\n *\n * Atomicity is provided by the JavaScript single-threaded event loop: each\n * favorite/unfavorite runs to completion within one synchronous block.\n */\nexport class InMemoryFavoritesStorage extends FavoritesStorage {\n  private db: InMemoryDB;\n\n  constructor({ db }: { db: InMemoryDB }) {\n    super();\n    this.db = db;\n  }\n\n  async init(): Promise<void> {\n    // No-op for in-memory store.\n  }\n\n  async dangerouslyClearAll(): Promise<void> {\n    this.db.favorites.clear();\n    // Keep denormalized counters in sync with the cleared favorites map.\n    for (const agent of this.db.agents.values()) {\n      if (agent.favoriteCount) agent.favoriteCount = 0;\n    }\n    for (const skill of this.db.skills.values()) {\n      if (skill.favoriteCount) skill.favoriteCount = 0;\n    }\n  }\n\n  async favorite({ userId, entityType, entityId }: StorageFavoriteKey): Promise<FavoriteToggleResult> {\n    const entity = this.requireEntity(entityType, entityId);\n    const key = favoriteKey(userId, entityType, entityId);\n\n    if (this.db.favorites.has(key)) {\n      return { favorited: true, favoriteCount: entity.favoriteCount ?? 0 };\n    }\n\n    const row: StorageFavoriteType = {\n      userId,\n      entityType,\n      entityId,\n      createdAt: new Date(),\n    };\n    this.db.favorites.set(key, row);\n\n    const nextCount = (entity.favoriteCount ?? 0) + 1;\n    entity.favoriteCount = nextCount;\n    entity.updatedAt = new Date();\n\n    return { favorited: true, favoriteCount: nextCount };\n  }\n\n  async unfavorite({ userId, entityType, entityId }: StorageFavoriteKey): Promise<FavoriteToggleResult> {\n    const entity = this.requireEntity(entityType, entityId);\n    const key = favoriteKey(userId, entityType, entityId);\n\n    if (!this.db.favorites.has(key)) {\n      return { favorited: false, favoriteCount: entity.favoriteCount ?? 0 };\n    }\n\n    this.db.favorites.delete(key);\n\n    const nextCount = Math.max(0, (entity.favoriteCount ?? 0) - 1);\n    entity.favoriteCount = nextCount;\n    entity.updatedAt = new Date();\n\n    return { favorited: false, favoriteCount: nextCount };\n  }\n\n  async isFavorited({ userId, entityType, entityId }: StorageFavoriteKey): Promise<boolean> {\n    return this.db.favorites.has(favoriteKey(userId, entityType, entityId));\n  }\n\n  async isFavoritedBatch({ userId, entityType, entityIds }: StorageIsFavoritedBatchInput): Promise<Set<string>> {\n    const result = new Set<string>();\n    for (const entityId of entityIds) {\n      if (this.db.favorites.has(favoriteKey(userId, entityType, entityId))) {\n        result.add(entityId);\n      }\n    }\n    return result;\n  }\n\n  async listFavoritedIds({ userId, entityType }: StorageListFavoritesInput): Promise<string[]> {\n    const ids: string[] = [];\n    for (const row of this.db.favorites.values()) {\n      if (row.userId === userId && row.entityType === entityType) {\n        ids.push(row.entityId);\n      }\n    }\n    return ids;\n  }\n\n  async deleteFavoritesForEntity({ entityType, entityId }: StorageDeleteFavoritesForEntityInput): Promise<number> {\n    let removed = 0;\n    for (const [key, row] of this.db.favorites) {\n      if (row.entityType === entityType && row.entityId === entityId) {\n        this.db.favorites.delete(key);\n        removed++;\n      }\n    }\n    // Zero the parent's denormalized counter if the record still exists. The\n    // cascade caller in the server typically deletes the entity first, in\n    // which case this is a no-op — but callers that prune favorites for a still\n    // existing entity (e.g. admin reset) need consistent counts.\n    const map = entityType === 'agent' ? this.db.agents : this.db.skills;\n    const entity = map.get(entityId);\n    if (entity && entity.favoriteCount) {\n      entity.favoriteCount = 0;\n    }\n    return removed;\n  }\n\n  /**\n   * Look up the parent entity record for counter maintenance. Throws if the\n   * entity does not exist — callers should validate existence (and access)\n   * before invoking favorite/unfavorite.\n   */\n  private requireEntity(\n    entityType: StorageFavoriteEntityType,\n    entityId: string,\n  ): { favoriteCount?: number; updatedAt: Date } {\n    const map = entityType === 'agent' ? this.db.agents : this.db.skills;\n    const entity = map.get(entityId);\n    if (!entity) {\n      throw new Error(`Cannot favorite: ${entityType} with id ${entityId} does not exist`);\n    }\n    return entity;\n  }\n}\n"],"mappings":"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"}