{"version":3,"file":"readable.mjs","sources":["../../../../src/query/helpers/streams/readable.ts"],"sourcesContent":["import { Readable } from 'stream';\nimport { isFinite } from 'lodash/fp';\nimport type { Knex } from 'knex';\nimport type { QueryBuilder } from '../../query-builder';\nimport type { Database } from '../../..';\n\nimport { applyPopulate } from '../populate';\nimport { fromRow } from '../transform';\nimport { Meta } from '../../../metadata';\n\nconst knexQueryDone = Symbol('knexQueryDone');\nconst knexPerformingQuery = Symbol('knexPerformingQuery');\n\ninterface ReadableStrapiQueryOptions {\n  qb: QueryBuilder;\n  uid: string;\n  db: Database;\n  mapResults?: boolean;\n  batchSize?: number;\n}\n\nclass ReadableStrapiQuery extends Readable {\n  _offset: number;\n\n  _limit: number | null;\n\n  _fetched: number;\n\n  _query: Knex.QueryBuilder;\n\n  _qb: QueryBuilder;\n\n  _db: Database;\n\n  _uid: string;\n\n  _meta: Meta;\n\n  _batchSize: number;\n\n  _mapResults: boolean;\n\n  [knexPerformingQuery]: boolean;\n\n  constructor({ qb, db, uid, mapResults = true, batchSize = 500 }: ReadableStrapiQueryOptions) {\n    super({ objectMode: true, highWaterMark: batchSize });\n\n    // Extract offset & limit from the query-builder's state\n    const { offset, limit } = qb.state;\n\n    // Original offset value\n    this._offset = isFinite(offset) ? Number(offset) : 0;\n\n    // Max amount of entities to fetch, force null as undefined value\n    this._limit = isFinite(limit) ? Number(limit) : null;\n\n    // Total amount of entities fetched\n    this._fetched = 0;\n\n    /**\n     * Original query\n     */\n    this._query = qb.getKnexQuery();\n\n    // Query Builder instance\n    this._qb = qb;\n\n    // Database related properties\n    this._db = db;\n    this._uid = uid;\n    this._meta = db.metadata.get(uid);\n\n    // Stream params\n    this._batchSize = batchSize;\n    this._mapResults = mapResults;\n\n    // States\n    this[knexPerformingQuery] = false;\n  }\n\n  _destroy(err: Error, cb: (err?: Error) => void) {\n    // If the stream is destroyed while a query is being made, then wait for a\n    // kQueryDone event to be emitted before actually destroying the stream\n    if (this[knexPerformingQuery]) {\n      this.once(knexQueryDone, (er) => cb(err || er));\n    } else {\n      cb(err);\n    }\n  }\n\n  /**\n   * Custom ._read() implementation\n   *\n   *  NOTE: Here \"size\" means the number of entities to be read from the database.\n   *  Not the actual byte size, as it would mean that we need to return partial entities.\n   *\n   */\n  async _read(size: number) {\n    const query = this._query;\n\n    // Remove the original offset & limit properties from the query\n    // Theoretically, they would be replaced by calling them again, but this is just to be sure\n    query.clear('limit').clear('offset');\n\n    // Define the maximum read size based on the limit and the requested size\n    // NOTE: size is equal to _batchSize by default. Since we want to allow customizing it on\n    // the fly, we need to use its value instead of batchSize when computing the maxReadSize value\n    const maxReadSize =\n      // if no limit is defined in the query, use the given size,\n      // otherwise, use the smallest value between the two\n      this._limit === null ? size : Math.min(size, this._limit);\n\n    // Compute the limit for the next query\n    const limit =\n      // If a limit is defined\n      this._limit !== null &&\n      // And reading `maxReadSize` would fetch too many entities (> _limit)\n      this._fetched + maxReadSize > this._limit\n        ? // Then adjust the limit so that it only get the remaining entities\n          this._limit - this._fetched\n        : // Else, use the max read size\n          maxReadSize;\n\n    // If we don't have anything left to read (_limit === _fetched),\n    // don't bother making the query and end the stream by pushing null\n    if (limit <= 0) {\n      this.push(null);\n      return;\n    }\n\n    // Compute the offset (base offset + number of entities already fetched)\n    const offset = this._offset + this._fetched;\n\n    // Update the query with the new values (offset + limit)\n    query.offset(offset).limit(limit);\n\n    // Lock the ._destroy()\n    this[knexPerformingQuery] = true;\n\n    let results;\n    let count;\n    let err;\n\n    try {\n      // Execute the query and store the results & count\n      results = await query;\n\n      const { populate } = this._qb.state;\n\n      // Applies the populate if needed\n      if (populate) {\n        await applyPopulate(results, populate, { qb: this._qb, uid: this._uid, db: this._db });\n      }\n\n      // Map results if asked to\n      if (this._mapResults) {\n        results = fromRow(this._meta, results);\n      }\n\n      count = results.length;\n    } catch (e) {\n      err = e;\n    }\n\n    // Unlock the ._destroy()\n    this[knexPerformingQuery] = false;\n\n    // Tell ._destroy() that it's now safe to close the db connection\n    if (this.destroyed) {\n      this.emit(knexQueryDone);\n      return;\n    }\n\n    // If there is an error, destroy with the given error\n    if (err) {\n      this.destroy(err as Error);\n      return;\n    }\n\n    // Update the amount of fetched entities\n    this._fetched += count;\n\n    // While there is at least one value to unpack\n    for (const result of results) {\n      this.push(result);\n    }\n\n    // If the amount of fetched entities is smaller than the\n    // maximum read size, Then push null to close the stream\n    if (this._fetched === this._limit || count < this._batchSize) {\n      this.push(null);\n    }\n  }\n}\n\nexport default ReadableStrapiQuery;\n"],"names":["knexQueryDone","Symbol","knexPerformingQuery","ReadableStrapiQuery","Readable","_destroy","err","cb","once","er","_read","size","query","_query","clear","maxReadSize","_limit","Math","min","limit","_fetched","push","offset","_offset","results","count","populate","_qb","state","applyPopulate","qb","uid","_uid","db","_db","_mapResults","fromRow","_meta","length","e","destroyed","emit","destroy","result","_batchSize","mapResults","batchSize","objectMode","highWaterMark","isFinite","Number","getKnexQuery","metadata","get"],"mappings":"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"}