{
  "version": 3,
  "sources": ["../src/RedisDriver.ts"],
  "sourcesContent": ["import {\n  Redis,\n  Cluster,\n  type ClusterNode,\n  type ClusterOptions,\n  type RedisOptions\n} from 'ioredis';\n\nimport {\n  type IRoomCache,\n  type MatchMakerDriver,\n  type SortOptions,\n  debugMatchMaking,\n  initializeRoomCache,\n} from '@colyseus/core';\n\nimport { Query } from './Query.ts';\n\nconst ROOMCACHES_KEY = 'roomcaches';\n\nexport class RedisDriver implements MatchMakerDriver {\n  private readonly _client: Redis | Cluster;\n\n  constructor(options?: number | string | RedisOptions | ClusterNode[] | Redis | Cluster, clusterOptions?: ClusterOptions) {\n    this._client = (options instanceof Redis || options instanceof Cluster)\n      ? options\n      : (Array.isArray(options))\n        ? new Cluster(options, clusterOptions)\n        : new Redis(options as RedisOptions);\n  }\n\n  public async has(roomId: string) {\n    return await this._client.hexists(ROOMCACHES_KEY, roomId) === 1;\n  }\n\n  public async query(conditions: Partial<IRoomCache>, sortOptions?: SortOptions) {\n    const query = new Query<IRoomCache>(this.getRooms(), conditions);\n\n    if (sortOptions) {\n      query.sort(sortOptions);\n    }\n\n    return query.all();\n\n  }\n\n  public async cleanup(processId: string) {\n    const cachedRooms = await this.query({ processId });\n    debugMatchMaking(\"removing stale rooms by processId %s (%s rooms found)\", processId, cachedRooms.length);\n\n    const itemsPerCommand = 500;\n\n    // remove rooms in batches of 500\n    for (let i = 0; i < cachedRooms.length; i += itemsPerCommand) {\n      await this._client.hdel(ROOMCACHES_KEY, ...cachedRooms.slice(i, i + itemsPerCommand).map((room) => room.roomId));\n    }\n  }\n\n  public findOne(conditions: Partial<IRoomCache>, sortOptions?: SortOptions): Promise<IRoomCache> {\n    if (typeof conditions.roomId !== 'undefined') {\n      // get room by roomId\n\n      //\n      // TODO: refactor driver APIs.\n      // the API here is legacy from MongooseDriver which made sense on versions <= 0.14.0\n      //\n\n      return new Promise<IRoomCache>((resolve, reject) => {\n        this._client.hget(ROOMCACHES_KEY, conditions.roomId).then((roomcache) => {\n          if (roomcache) {\n            resolve(initializeRoomCache(JSON.parse(roomcache)));\n          } else {\n            resolve(undefined);\n          }\n        }).catch(reject);\n      });\n\n    } else {\n      // filter list by other conditions\n      const query = new Query<IRoomCache>(this.getRooms(conditions['name']), conditions);\n\n      if (sortOptions) {\n        query.sort(sortOptions);\n      }\n\n      return query as any as Promise<IRoomCache>;\n    }\n  }\n\n  // gets recent room caches w/o making multiple simultaneous reads to REDIS\n  private _concurrentRoomCacheRequest?: Promise<Record<string, string>>;\n  private _roomCacheRequestByName: {[roomName: string]: Promise<IRoomCache[]>} = {};\n  private getRooms(roomName?: string) {\n    // if there's a shared request, return it\n    if (this._roomCacheRequestByName[roomName] !== undefined) {\n      return this._roomCacheRequestByName[roomName];\n    }\n\n    const roomCacheRequest = this._concurrentRoomCacheRequest || this._client.hgetall(ROOMCACHES_KEY);\n    this._concurrentRoomCacheRequest = roomCacheRequest;\n\n    this._roomCacheRequestByName[roomName] = roomCacheRequest.then((result) => {\n      // clear shared promises so we can read it again\n      this._concurrentRoomCacheRequest = undefined;\n      delete this._roomCacheRequestByName[roomName];\n\n      let roomcaches = Object.entries(result ?? {});\n\n      //\n      // micro optimization:\n      // filter rooms by name before parsing JSON\n      //\n      if (roomName !== undefined) {\n        const roomNameField = `\"name\":\"${roomName}\"`;\n        roomcaches = roomcaches.filter(([, roomcache]) => (roomcache as string).includes(roomNameField));\n      }\n\n      return roomcaches.map(\n        ([, roomcache]) => initializeRoomCache(JSON.parse(roomcache as string))\n      );\n    });\n\n    return this._roomCacheRequestByName[roomName];\n  }\n\n  public async update(room: IRoomCache, operations: Partial<{ $set: Partial<IRoomCache>, $inc: Partial<IRoomCache> }>) {\n    if (operations.$set) {\n      for (const field in operations.$set) {\n        if (operations.$set.hasOwnProperty(field)) {\n          room[field] = operations.$set[field];\n        }\n      }\n    }\n\n    if (operations.$inc) {\n      for (const field in operations.$inc) {\n        if (operations.$inc.hasOwnProperty(field)) {\n          room[field] += operations.$inc[field];\n        }\n      }\n    }\n\n    await this._client.hset(ROOMCACHES_KEY, room.roomId, JSON.stringify(room));\n    return true;\n  }\n\n  public async persist(room: IRoomCache, _: boolean = false) {\n    if (!room.roomId) {\n      debugMatchMaking(\"RedisDriver: can't .persist() without a `roomId`\");\n      return false;\n    }\n\n    await this._client.hset(ROOMCACHES_KEY, room.roomId, JSON.stringify(room));\n    return true;\n  }\n\n  public async remove(roomId: string) {\n    const result = await this._client.hdel(ROOMCACHES_KEY, roomId);\n    return result > 0;\n  }\n\n  public async shutdown() {\n    await this._client.quit();\n  }\n\n  //\n  // only relevant for the test-suite.\n  // not used during runtime.\n  //\n  public clear() {\n    this._client.del(ROOMCACHES_KEY);\n  }\n\n}"],
  "mappings": "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",
  "names": []
}
