( repoOwner: string, repoName: string, releaseTagName: string )
| 28 | * could not be found. |
| 29 | */ |
| 30 | export async function getReleaseNotesAfter( |
| 31 | repoOwner: string, |
| 32 | repoName: string, |
| 33 | releaseTagName: string |
| 34 | ): Promise<Release[]> { |
| 35 | const response = await requestUrl({ |
| 36 | url: `https://api.github.com/repos/${repoOwner}/${repoName}/releases`, |
| 37 | throw: false, |
| 38 | }); |
| 39 | |
| 40 | const body: unknown = response.json; |
| 41 | |
| 42 | // Treat any error status OR a non-array body as a failure. GitHub returns the |
| 43 | // release list (a JSON array) only on success and a `{ message }` object on |
| 44 | // error, but with `throw: false` an error status still reaches here - and an |
| 45 | // intermediary can return an error status alongside a stale array or a literal |
| 46 | // `null`/primitive. Read `.message` only when it's actually a string, so a |
| 47 | // null/primitive body degrades to "Unknown error" instead of throwing a |
| 48 | // TypeError (`null.message` / `"message" in null`) that would mask the real |
| 49 | // failure. |
| 50 | if (response.status >= 400 || !Array.isArray(body)) { |
| 51 | const message = |
| 52 | typeof body === "object" && |
| 53 | body !== null && |
| 54 | "message" in body && |
| 55 | typeof (body as { message?: unknown }).message === "string" |
| 56 | ? (body as { message: string }).message |
| 57 | : "Unknown error"; |
| 58 | throw new Error(`Failed to fetch releases: ${message}`); |
| 59 | } |
| 60 | |
| 61 | const releases = body as Release[]; |
| 62 | |
| 63 | const startReleaseIdx = releases.findIndex( |
| 64 | (release) => release.tag_name === releaseTagName |
| 65 | ); |
| 66 | |
| 67 | if (startReleaseIdx === -1) { |
| 68 | throw new Error(`Could not find release with tag ${releaseTagName}`); |
| 69 | } |
| 70 | |
| 71 | return releases |
| 72 | .slice(0, startReleaseIdx) |
| 73 | .filter((release) => !release.draft && !release.prerelease); |
| 74 | } |
| 75 | |
| 76 | const USER_ATTACHMENT_VIDEO_URL = |
| 77 | /^https:\/\/github\.com\/user-attachments\/assets\/[A-Za-z0-9-]+$/; |
no test coverage detected