{
  "version": 3,
  "sources": ["../src/RedisDriver.ts"],
  "sourcesContent": ["import Redis, { Cluster, ClusterNode, ClusterOptions, RedisOptions } from 'ioredis';\n\nimport {\n  IRoomCache,\n  MatchMakerDriver,\n  SortOptions,\n  debugMatchMaking,\n} from '@colyseus/core';\n\nimport { Query } from './Query.js';\nimport { RoomData } from './RoomData.js';\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[], clusterOptions?: ClusterOptions) {\n    this._client = (Array.isArray(options))\n      ? new Cluster(options, clusterOptions)\n      : new Redis(options as RedisOptions);\n  }\n\n  public createInstance(initialValues: Partial<IRoomCache> = {}) {\n    return new RoomData(initialValues, this._client);\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 rooms = await this.getRooms();\n    return rooms.filter((room) => {\n      if (!room.roomId) {\n        return false;\n      }\n\n      for (const field in conditions) {\n        if (\n          conditions.hasOwnProperty(field) &&\n          room[field] !== conditions[field]\n        ) {\n          return false;\n        }\n      }\n      return true;\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<RoomData> {\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      // @ts-ignore\n      return new Promise<IRoomCache>((resolve, reject) => {\n        this._client.hget(ROOMCACHES_KEY, conditions.roomId).then((roomcache) => {\n          if (roomcache) {\n            resolve(new RoomData(JSON.parse(roomcache), this._client));\n          } else {\n            resolve(undefined);\n          }\n        }).catch(reject);\n      });\n\n    } else {\n      // filter list by other conditions\n      const query = new Query<RoomData>(this.getRooms(conditions['name']), conditions);\n\n      if (sortOptions) {\n        query.sort(sortOptions);\n      }\n\n      return query as any as Promise<RoomData>;\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<RoomData[]>} = {};\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.includes(roomNameField));\n      }\n\n      return roomcaches.map(\n        // TODO: probably no need to instantiate RoomData here.\n        ([, roomcache]) => new RoomData(JSON.parse(roomcache), this._client)\n      );\n    });\n\n    return this._roomCacheRequestByName[roomName];\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}\n"],
  "mappings": "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",
  "names": []
}
