{"version":3,"sources":["../src/releases/index.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\r\nimport {\r\n  APP_DISTRIBUTION_ENDPOINT,\r\n  ENDPOINT_VERSION,\r\n  makeRequest,\r\n} from \"../utils\";\r\nimport { Operation, Operations, Release } from \"./operations\";\r\nimport { FirebaseAppDistribution } from \"../\";\r\n\r\ninterface ListReleasesOptions {\r\n  pageSize?: number;\r\n  orderBy?: string;\r\n  filter?: string;\r\n  maxPages?: number;\r\n}\r\n\r\ninterface ListReleasesResponse {\r\n  releases: Release[];\r\n  nextPageToken: string;\r\n}\r\n\r\ninterface DistributeReleaseOptions {\r\n  testerEmails: string[];\r\n  groupAliases: string[];\r\n}\r\n\r\ninterface UpdateReleaseOptions {\r\n  releaseNotes?: string;\r\n}\r\n\r\nfunction isValidReleaseName(releaseName: string): boolean {\r\n  const regex = /^projects\\/[^/]+\\/apps\\/[^/]+\\/releases\\/[^/]+$/;\r\n  return regex.test(releaseName);\r\n}\r\n\r\nfunction validateReleaseName(releaseName: string) {\r\n  if (!isValidReleaseName(releaseName)) {\r\n    throw new Error(\r\n      `Invalid release name: ${releaseName}. Expected 'projects/*/apps/*/releases/*'`,\r\n    );\r\n  }\r\n  return;\r\n}\r\n\r\nexport class Releases {\r\n  private parent: FirebaseAppDistribution;\r\n\r\n  operations: Operations;\r\n\r\n  constructor(parent: FirebaseAppDistribution) {\r\n    this.parent = parent;\r\n    this.operations = new Operations(this.parent);\r\n  }\r\n\r\n  public async upload(\r\n    appId: string,\r\n    binaryPath: string,\r\n    fileName: string,\r\n  ): Promise<Operation> {\r\n    const accessToken = await this.parent.getAccessToken();\r\n    const projectNumber = await this.parent.getProjectNumber();\r\n\r\n    const url = new URL(\r\n      `${APP_DISTRIBUTION_ENDPOINT}/upload/${ENDPOINT_VERSION}/projects/${projectNumber}/apps/${appId}/releases:upload`,\r\n    );\r\n    const fileBuffer = readFileSync(binaryPath);\r\n\r\n    const response = await makeRequest<Operation>(url.toString(), {\r\n      method: \"POST\",\r\n      headers: {\r\n        Authorization: `Bearer ${accessToken}`,\r\n        \"X-Goog-Upload-File-Name\": encodeURIComponent(fileName),\r\n        \"X-Goog-Upload-Protocol\": \"raw\",\r\n      },\r\n      body: new Uint8Array(fileBuffer),\r\n    });\r\n\r\n    return response;\r\n  }\r\n\r\n  public async delete(appId: string, releaseNames: string[]): Promise<{}> {\r\n    for (let releaseName of releaseNames) {\r\n      validateReleaseName(releaseName);\r\n    }\r\n\r\n    const accessToken = await this.parent.getAccessToken();\r\n    const projectNumber = await this.parent.getProjectNumber();\r\n\r\n    if (releaseNames.length > 100) {\r\n      throw new Error(\"Cannot delete more than 100 releases at once\");\r\n    }\r\n\r\n    const url = new URL(\r\n      `${APP_DISTRIBUTION_ENDPOINT}/${ENDPOINT_VERSION}/projects/${projectNumber}/apps/${appId}/releases:batchDelete`,\r\n    );\r\n\r\n    const response = await makeRequest<{}>(url.toString(), {\r\n      method: \"POST\",\r\n      headers: {\r\n        Authorization: `Bearer ${accessToken}`,\r\n      },\r\n      body: JSON.stringify({\r\n        names: releaseNames,\r\n      }),\r\n    });\r\n\r\n    return response;\r\n  }\r\n\r\n  public async distribute(\r\n    releaseName: string,\r\n    { testerEmails = [], groupAliases = [] }: DistributeReleaseOptions,\r\n  ) {\r\n    validateReleaseName(releaseName);\r\n    if (testerEmails.length === 0 && groupAliases.length === 0) {\r\n      throw new Error(\"Must specify at least one tester or group\");\r\n    }\r\n    const accessToken = await this.parent.getAccessToken();\r\n\r\n    const url = new URL(\r\n      `${APP_DISTRIBUTION_ENDPOINT}/${ENDPOINT_VERSION}/${releaseName}:distribute`,\r\n    );\r\n\r\n    const response = await makeRequest<{}>(url.toString(), {\r\n      method: \"POST\",\r\n      headers: {\r\n        Authorization: `Bearer ${accessToken}`,\r\n      },\r\n      body: JSON.stringify({\r\n        testerEmails,\r\n        groupAliases,\r\n      }),\r\n    });\r\n\r\n    return response;\r\n  }\r\n\r\n  public async get(releaseName: string) {\r\n    validateReleaseName(releaseName);\r\n    const accessToken = await this.parent.getAccessToken();\r\n\r\n    const url = new URL(\r\n      `${APP_DISTRIBUTION_ENDPOINT}/${ENDPOINT_VERSION}/${releaseName}`,\r\n    );\r\n\r\n    const response = await makeRequest<Release>(url.toString(), {\r\n      method: \"GET\",\r\n      headers: {\r\n        Authorization: `Bearer ${accessToken}`,\r\n      },\r\n    });\r\n\r\n    return response;\r\n  }\r\n\r\n  public async list(\r\n    appId: string,\r\n    { pageSize, orderBy, filter, maxPages }: ListReleasesOptions = {},\r\n  ): Promise<Release[]> {\r\n    const accessToken = await this.parent.getAccessToken();\r\n    const projectNumber = await this.parent.getProjectNumber();\r\n\r\n    const url = new URL(\r\n      `${APP_DISTRIBUTION_ENDPOINT}/${ENDPOINT_VERSION}/projects/${projectNumber}/apps/${appId}/releases`,\r\n    );\r\n\r\n    if (pageSize) {\r\n      url.searchParams.set(\"pageSize\", pageSize?.toString() || \"100\");\r\n    }\r\n    if (orderBy) {\r\n      url.searchParams.set(\"orderBy\", orderBy);\r\n    }\r\n    if (filter) {\r\n      url.searchParams.set(\"filter\", filter);\r\n    }\r\n\r\n    let nextPageToken = \"\";\r\n    let currentPage = 0;\r\n    const releases: Release[] = [];\r\n    do {\r\n      if (nextPageToken) {\r\n        url.searchParams.set(\"pageToken\", nextPageToken);\r\n      } else {\r\n        url.searchParams.delete(\"pageToken\");\r\n      }\r\n\r\n      const response = await makeRequest<ListReleasesResponse>(url.toString(), {\r\n        method: \"GET\",\r\n        headers: {\r\n          Authorization: `Bearer ${accessToken}`,\r\n        },\r\n      });\r\n\r\n      if (response.releases === undefined) {\r\n        break;\r\n      }\r\n\r\n      releases.push(...response.releases);\r\n      if (response.nextPageToken !== undefined) {\r\n        nextPageToken = response.nextPageToken;\r\n      } else {\r\n        break;\r\n      }\r\n\r\n      currentPage++;\r\n      if (maxPages && currentPage >= maxPages) {\r\n        break;\r\n      }\r\n    } while (nextPageToken);\r\n\r\n    return releases;\r\n  }\r\n\r\n  async #update(\r\n    releaseName: string,\r\n    { releaseNotes }: UpdateReleaseOptions = {},\r\n  ) {\r\n    validateReleaseName(releaseName);\r\n    const accessToken = await this.parent.getAccessToken();\r\n\r\n    const url = new URL(\r\n      `${APP_DISTRIBUTION_ENDPOINT}/${ENDPOINT_VERSION}/${releaseName}`,\r\n    );\r\n\r\n    if (releaseNotes) {\r\n      url.searchParams.set(\"updateMask\", \"releaseNotes.text\");\r\n    }\r\n\r\n    const response = await makeRequest<Release>(url.toString(), {\r\n      method: \"PATCH\",\r\n      headers: {\r\n        Authorization: `Bearer ${accessToken}`,\r\n      },\r\n      body: JSON.stringify({\r\n        releaseNotes: {\r\n          text: releaseNotes,\r\n        },\r\n      }),\r\n    });\r\n\r\n    return response;\r\n  }\r\n\r\n  public async addReleaseNotes(releaseName: string, releaseNotes: string) {\r\n    return this.#update(releaseName, { releaseNotes });\r\n  }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;AAAA,SAAS,oBAAoB;AA8B7B,SAAS,mBAAmB,aAA8B;AACxD,QAAM,QAAQ;AACd,SAAO,MAAM,KAAK,WAAW;AAC/B;AAEA,SAAS,oBAAoB,aAAqB;AAChD,MAAI,CAAC,mBAAmB,WAAW,GAAG;AACpC,UAAM,IAAI;AAAA,MACR,yBAAyB,WAAW;AAAA,IACtC;AAAA,EACF;AACA;AACF;AA1CA;AA4CO,IAAM,WAAN,MAAe;AAAA,EAKpB,YAAY,QAAiC;AALxC;AAMH,SAAK,SAAS;AACd,SAAK,aAAa,IAAI,WAAW,KAAK,MAAM;AAAA,EAC9C;AAAA,EAEa,OACX,OACA,YACA,UACoB;AAAA;AACpB,YAAM,cAAc,MAAM,KAAK,OAAO,eAAe;AACrD,YAAM,gBAAgB,MAAM,KAAK,OAAO,iBAAiB;AAEzD,YAAM,MAAM,IAAI;AAAA,QACd,GAAG,yBAAyB,WAAW,gBAAgB,aAAa,aAAa,SAAS,KAAK;AAAA,MACjG;AACA,YAAM,aAAa,aAAa,UAAU;AAE1C,YAAM,WAAW,MAAM,YAAuB,IAAI,SAAS,GAAG;AAAA,QAC5D,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,UAAU,WAAW;AAAA,UACpC,2BAA2B,mBAAmB,QAAQ;AAAA,UACtD,0BAA0B;AAAA,QAC5B;AAAA,QACA,MAAM,IAAI,WAAW,UAAU;AAAA,MACjC,CAAC;AAED,aAAO;AAAA,IACT;AAAA;AAAA,EAEa,OAAO,OAAe,cAAqC;AAAA;AACtE,eAAS,eAAe,cAAc;AACpC,4BAAoB,WAAW;AAAA,MACjC;AAEA,YAAM,cAAc,MAAM,KAAK,OAAO,eAAe;AACrD,YAAM,gBAAgB,MAAM,KAAK,OAAO,iBAAiB;AAEzD,UAAI,aAAa,SAAS,KAAK;AAC7B,cAAM,IAAI,MAAM,8CAA8C;AAAA,MAChE;AAEA,YAAM,MAAM,IAAI;AAAA,QACd,GAAG,yBAAyB,IAAI,gBAAgB,aAAa,aAAa,SAAS,KAAK;AAAA,MAC1F;AAEA,YAAM,WAAW,MAAM,YAAgB,IAAI,SAAS,GAAG;AAAA,QACrD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,UAAU,WAAW;AAAA,QACtC;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,OAAO;AAAA,QACT,CAAC;AAAA,MACH,CAAC;AAED,aAAO;AAAA,IACT;AAAA;AAAA,EAEa,WACX,IACA,IACA;AAAA,+CAFA,aACA,EAAE,eAAe,CAAC,GAAG,eAAe,CAAC,EAAE,GACvC;AACA,0BAAoB,WAAW;AAC/B,UAAI,aAAa,WAAW,KAAK,aAAa,WAAW,GAAG;AAC1D,cAAM,IAAI,MAAM,2CAA2C;AAAA,MAC7D;AACA,YAAM,cAAc,MAAM,KAAK,OAAO,eAAe;AAErD,YAAM,MAAM,IAAI;AAAA,QACd,GAAG,yBAAyB,IAAI,gBAAgB,IAAI,WAAW;AAAA,MACjE;AAEA,YAAM,WAAW,MAAM,YAAgB,IAAI,SAAS,GAAG;AAAA,QACrD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,UAAU,WAAW;AAAA,QACtC;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAED,aAAO;AAAA,IACT;AAAA;AAAA,EAEa,IAAI,aAAqB;AAAA;AACpC,0BAAoB,WAAW;AAC/B,YAAM,cAAc,MAAM,KAAK,OAAO,eAAe;AAErD,YAAM,MAAM,IAAI;AAAA,QACd,GAAG,yBAAyB,IAAI,gBAAgB,IAAI,WAAW;AAAA,MACjE;AAEA,YAAM,WAAW,MAAM,YAAqB,IAAI,SAAS,GAAG;AAAA,QAC1D,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,UAAU,WAAW;AAAA,QACtC;AAAA,MACF,CAAC;AAED,aAAO;AAAA,IACT;AAAA;AAAA,EAEa,KACX,IAEoB;AAAA,+CAFpB,OACA,EAAE,UAAU,SAAS,QAAQ,SAAS,IAAyB,CAAC,GAC5C;AACpB,YAAM,cAAc,MAAM,KAAK,OAAO,eAAe;AACrD,YAAM,gBAAgB,MAAM,KAAK,OAAO,iBAAiB;AAEzD,YAAM,MAAM,IAAI;AAAA,QACd,GAAG,yBAAyB,IAAI,gBAAgB,aAAa,aAAa,SAAS,KAAK;AAAA,MAC1F;AAEA,UAAI,UAAU;AACZ,YAAI,aAAa,IAAI,aAAY,qCAAU,eAAc,KAAK;AAAA,MAChE;AACA,UAAI,SAAS;AACX,YAAI,aAAa,IAAI,WAAW,OAAO;AAAA,MACzC;AACA,UAAI,QAAQ;AACV,YAAI,aAAa,IAAI,UAAU,MAAM;AAAA,MACvC;AAEA,UAAI,gBAAgB;AACpB,UAAI,cAAc;AAClB,YAAM,WAAsB,CAAC;AAC7B,SAAG;AACD,YAAI,eAAe;AACjB,cAAI,aAAa,IAAI,aAAa,aAAa;AAAA,QACjD,OAAO;AACL,cAAI,aAAa,OAAO,WAAW;AAAA,QACrC;AAEA,cAAM,WAAW,MAAM,YAAkC,IAAI,SAAS,GAAG;AAAA,UACvE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,eAAe,UAAU,WAAW;AAAA,UACtC;AAAA,QACF,CAAC;AAED,YAAI,SAAS,aAAa,QAAW;AACnC;AAAA,QACF;AAEA,iBAAS,KAAK,GAAG,SAAS,QAAQ;AAClC,YAAI,SAAS,kBAAkB,QAAW;AACxC,0BAAgB,SAAS;AAAA,QAC3B,OAAO;AACL;AAAA,QACF;AAEA;AACA,YAAI,YAAY,eAAe,UAAU;AACvC;AAAA,QACF;AAAA,MACF,SAAS;AAET,aAAO;AAAA,IACT;AAAA;AAAA,EAgCa,gBAAgB,aAAqB,cAAsB;AAAA;AACtE,aAAO,sBAAK,gCAAL,WAAa,aAAa,EAAE,aAAa;AAAA,IAClD;AAAA;AACF;AA1MO;AAyKC,YAAO,SACX,IAEA;AAAA,6CAFA,aACA,EAAE,aAAa,IAA0B,CAAC,GAC1C;AACA,wBAAoB,WAAW;AAC/B,UAAM,cAAc,MAAM,KAAK,OAAO,eAAe;AAErD,UAAM,MAAM,IAAI;AAAA,MACd,GAAG,yBAAyB,IAAI,gBAAgB,IAAI,WAAW;AAAA,IACjE;AAEA,QAAI,cAAc;AAChB,UAAI,aAAa,IAAI,cAAc,mBAAmB;AAAA,IACxD;AAEA,UAAM,WAAW,MAAM,YAAqB,IAAI,SAAS,GAAG;AAAA,MAC1D,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,WAAW;AAAA,MACtC;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,cAAc;AAAA,UACZ,MAAM;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,WAAO;AAAA,EACT;AAAA;","names":[]}