{"version":3,"file":"expirationManager.mjs","names":["client: S3","bucket: string","expirationPeriodInMilliseconds: number","generateInfoKey: S3MetadataManager['generateInfoKey']","generatePartKey: S3MetadataManager['generatePartKey']","keyMarker: string | undefined","uploadIdMarker: string | undefined","listResponse: AWS.ListMultipartUploadsCommandOutput","deletions: Promise<AWS.DeleteObjectsCommandOutput>[]"],"sources":["../../../../src/tus/stores/s3/expirationManager.ts"],"sourcesContent":["import type AWS from '@aws-sdk/client-s3'\nimport type { S3 } from '@aws-sdk/client-s3'\nimport type { S3MetadataManager } from './metadataManager'\n\ntype GetExpirationDateArgs = {\n  createdAt: string\n}\n\nexport class S3ExpirationManager {\n  constructor(\n    private client: S3,\n    private bucket: string,\n    private expirationPeriodInMilliseconds: number,\n    private generateInfoKey: S3MetadataManager['generateInfoKey'],\n    private generatePartKey: S3MetadataManager['generatePartKey']\n  ) {}\n\n  /**\n   * Calculates the expiration date for a file based on a creation date\n   * @param args - The function arguments\n   * @param args.createdAt - The creation date as a string\n   * @returns The expiration date\n   */\n  getExpirationDate(args: GetExpirationDateArgs): Date {\n    const { createdAt } = args\n    const date = new Date(createdAt)\n    return new Date(date.getTime() + this.expirationPeriodInMilliseconds)\n  }\n\n  /**\n   * Gets the expiration period in milliseconds\n   * @returns The expiration period in milliseconds\n   */\n  getExpiration(): number {\n    return this.expirationPeriodInMilliseconds\n  }\n\n  /**\n   * Deletes expired incomplete uploads based on their initialization date.\n   * Returns the number of deleted uploads.\n   * @param args - The function arguments (empty object)\n   * @returns Promise that resolves to the number of deleted uploads\n   */\n  async deleteExpired(): Promise<number> {\n    // No arguments to destructure\n    if (this.getExpiration() === 0) {\n      return 0\n    }\n\n    let keyMarker: string | undefined = undefined\n    let uploadIdMarker: string | undefined = undefined\n    let isTruncated = true\n    let deleted = 0\n\n    while (isTruncated) {\n      const listResponse: AWS.ListMultipartUploadsCommandOutput =\n        await this.client.listMultipartUploads({\n          Bucket: this.bucket,\n          KeyMarker: keyMarker,\n          UploadIdMarker: uploadIdMarker,\n        })\n\n      const expiredUploads =\n        listResponse.Uploads?.filter((multiPartUpload: AWS.MultipartUpload) => {\n          const initiatedDate = multiPartUpload.Initiated\n          return (\n            initiatedDate &&\n            new Date().getTime() >\n              this.getExpirationDate({\n                createdAt: initiatedDate.toISOString(),\n              }).getTime()\n          )\n        }) || []\n\n      const objectsToDelete = expiredUploads.reduce(\n        (all: { Key: string }[], expiredUpload: AWS.MultipartUpload) => {\n          all.push(\n            {\n              Key: this.generateInfoKey({ id: expiredUpload.Key as string }),\n            },\n            {\n              Key: this.generatePartKey({\n                id: expiredUpload.Key as string,\n                isIncomplete: true,\n              }),\n            }\n          )\n          return all\n        },\n        [] as { Key: string }[]\n      )\n\n      const deletions: Promise<AWS.DeleteObjectsCommandOutput>[] = []\n\n      if (objectsToDelete.length > 0) {\n        deletions.push(\n          this.client.deleteObjects({\n            Bucket: this.bucket,\n            Delete: {\n              Objects: objectsToDelete,\n            },\n          })\n        )\n      }\n\n      const abortions = expiredUploads.map(\n        (expiredUpload: AWS.MultipartUpload) =>\n          this.client.abortMultipartUpload({\n            Bucket: this.bucket,\n            Key: expiredUpload.Key,\n            UploadId: expiredUpload.UploadId,\n          })\n      )\n\n      await Promise.all([...deletions, ...abortions])\n\n      deleted += expiredUploads.length\n\n      isTruncated = listResponse.IsTruncated || false\n      keyMarker = listResponse.NextKeyMarker\n      uploadIdMarker = listResponse.NextUploadIdMarker\n    }\n\n    return deleted\n  }\n}\n"],"mappings":";AAQA,IAAa,sBAAb,MAAiC;CAC/B,YACE,AAAQA,QACR,AAAQC,QACR,AAAQC,gCACR,AAAQC,iBACR,AAAQC,iBACR;EALQ;EACA;EACA;EACA;EACA;;;;;;;;CASV,kBAAkB,MAAmC;EACnD,MAAM,EAAE,cAAc;EACtB,MAAM,OAAO,IAAI,KAAK,UAAU;AAChC,SAAO,IAAI,KAAK,KAAK,SAAS,GAAG,KAAK,+BAA+B;;;;;;CAOvE,gBAAwB;AACtB,SAAO,KAAK;;;;;;;;CASd,MAAM,gBAAiC;AAErC,MAAI,KAAK,eAAe,KAAK,EAC3B,QAAO;EAGT,IAAIC,YAAgC;EACpC,IAAIC,iBAAqC;EACzC,IAAI,cAAc;EAClB,IAAI,UAAU;AAEd,SAAO,aAAa;GAClB,MAAMC,eACJ,MAAM,KAAK,OAAO,qBAAqB;IACrC,QAAQ,KAAK;IACb,WAAW;IACX,gBAAgB;IACjB,CAAC;GAEJ,MAAM,iBACJ,aAAa,SAAS,QAAQ,oBAAyC;IACrE,MAAM,gBAAgB,gBAAgB;AACtC,WACE,kCACA,IAAI,MAAM,EAAC,SAAS,GAClB,KAAK,kBAAkB,EACrB,WAAW,cAAc,aAAa,EACvC,CAAC,CAAC,SAAS;KAEhB,IAAI,EAAE;GAEV,MAAM,kBAAkB,eAAe,QACpC,KAAwB,kBAAuC;AAC9D,QAAI,KACF,EACE,KAAK,KAAK,gBAAgB,EAAE,IAAI,cAAc,KAAe,CAAC,EAC/D,EACD,EACE,KAAK,KAAK,gBAAgB;KACxB,IAAI,cAAc;KAClB,cAAc;KACf,CAAC,EACH,CACF;AACD,WAAO;MAET,EAAE,CACH;GAED,MAAMC,YAAuD,EAAE;AAE/D,OAAI,gBAAgB,SAAS,EAC3B,WAAU,KACR,KAAK,OAAO,cAAc;IACxB,QAAQ,KAAK;IACb,QAAQ,EACN,SAAS,iBACV;IACF,CAAC,CACH;GAGH,MAAM,YAAY,eAAe,KAC9B,kBACC,KAAK,OAAO,qBAAqB;IAC/B,QAAQ,KAAK;IACb,KAAK,cAAc;IACnB,UAAU,cAAc;IACzB,CAAC,CACL;AAED,SAAM,QAAQ,IAAI,CAAC,GAAG,WAAW,GAAG,UAAU,CAAC;AAE/C,cAAW,eAAe;AAE1B,iBAAc,aAAa,eAAe;AAC1C,eAAY,aAAa;AACzB,oBAAiB,aAAa;;AAGhC,SAAO"}