Skip to content

Commit cf8c794

Browse files
committed
Reworked the version select code a little bit
1 parent 2a6c306 commit cf8c794

4 files changed

Lines changed: 153 additions & 22 deletions

File tree

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
},
1313
"dependencies": {
1414
"@jcubic/wayne": "~0.19.1",
15+
"@octokit/openapi-types": "^27.0.0",
16+
"@octokit/types": "^16.0.0",
1517
"@vite-pwa/astro": "^1.0.0",
1618
"@zenfs/archives": "^1.1.0",
1719
"@zenfs/core": "^2.0.0",
@@ -30,5 +32,5 @@
3032
"tinyexec": "^1.0.1",
3133
"vite": "^6.3.7"
3234
},
33-
"packageManager": "pnpm@11.6.0+sha512.9a36518224080c6fe5165afdcfe79bfa118c29be703f3f462b1e32efe1e98e47e8750b148e08286250aad4113cc7993ca413c4e2cd447752708c2ee5751bc95f"
35+
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b"
3436
}

pnpm-lock.yaml

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/lib/github-releases-api.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import type { paths as $paths } from "@octokit/openapi-types";
2+
3+
type ReleaseInfo = $paths["/repos/{owner}/{repo}/releases"]["get"];
4+
type PotentialReleasesResponses = ReleaseInfo["responses"];
5+
6+
7+
/** @license Apache-2.0 */
8+
interface TypedHeaders<HeadersRecord extends Record<string, string>> extends Headers {
9+
append<Header extends keyof HeadersRecord>(name: Header, value: HeadersRecord[Header]): void;
10+
delete(name: keyof HeadersRecord): void;
11+
12+
get<
13+
Header extends ((string & {}) | keyof HeadersRecord)
14+
>(name: Header):
15+
Header extends keyof HeadersRecord
16+
? HeadersRecord[Header]
17+
: null;
18+
has(name: keyof HeadersRecord): name is keyof HeadersRecord;
19+
set<Header extends keyof HeadersRecord>(name: Header, value: HeadersRecord[Header]): void;
20+
21+
forEach(
22+
callbackfn: <Header extends Exclude<keyof HeadersRecord, symbol | number>>(
23+
value: HeadersRecord[Header],
24+
key: Header,
25+
parent: TypedHeaders<HeadersRecord>,
26+
) => void,
27+
thisArg?: any,
28+
): void;
29+
30+
31+
[Symbol.iterator]<Keys extends keyof HeadersRecord>(): HeadersIterator<[Keys, HeadersRecord[Keys]]>;
32+
entries<Keys extends keyof HeadersRecord>(): HeadersIterator<[Keys, HeadersRecord[Keys]]>;
33+
34+
keys(): HeadersIterator<Exclude<keyof HeadersRecord, number | symbol>>;
35+
values(): HeadersIterator<HeadersRecord[keyof HeadersRecord]>;
36+
}
37+
38+
39+
/** @license Apache-2.0 */
40+
interface TypedResponse<
41+
StatusCode extends Response["status"],
42+
CustomHeaders extends TypedHeaders<Record<string, string>> = TypedHeaders<Record<string, string>>,
43+
Url extends Response["url"] = Response["url"],
44+
> extends Response {
45+
readonly status: StatusCode;
46+
readonly ok: StatusCode extends 200 | 201 | 202 | 204 | 205 ? true : false;
47+
48+
readonly url: Url;
49+
readonly headers: CustomHeaders;
50+
51+
clone(): this;
52+
}
53+
54+
interface TypedJsonResponse<
55+
StatusCode extends Response["status"],
56+
Data extends unknown,
57+
CustomHeaders extends TypedHeaders<Record<string, string>> = TypedHeaders<Record<string, string>>,
58+
Url extends Response["url"] = Response["url"],
59+
> extends TypedResponse<StatusCode, CustomHeaders, Url> {
60+
json(): Promise<Data>;
61+
}
62+
63+
64+
65+
import type { ResponseHeaders as $ResponseHeaders } from "@octokit/types";
66+
67+
type CleanGithubResponseHeaders = {
68+
[Header in keyof $ResponseHeaders as string extends Header ? never : Header]: string;
69+
};
70+
71+
72+
73+
export type ReleasesResponse<
74+
StatusCode extends keyof PotentialReleasesResponses = keyof PotentialReleasesResponses,
75+
Content extends PotentialReleasesResponses[StatusCode]["content"] = PotentialReleasesResponses[StatusCode]["content"]
76+
> = keyof Content extends "application/json"
77+
? TypedJsonResponse<
78+
StatusCode,
79+
Content["application/json"],
80+
TypedHeaders<CleanGithubResponseHeaders>
81+
>
82+
: TypedResponse<
83+
StatusCode,
84+
TypedHeaders<CleanGithubResponseHeaders>
85+
>;

src/lib/sw-loader.ts

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ if ("serviceWorker" in navigator) {
5151
}
5252

5353

54+
5455
type VersionInfo = [
5556
string,
5657
{
@@ -59,29 +60,53 @@ type VersionInfo = [
5960
}
6061
];
6162

62-
type ReleasesAPI = {
63-
assets: [{
64-
name: string;
65-
browser_download_url: string
66-
}];
67-
name: string;
68-
tag_name: string;
69-
};
70-
7163
declare global {
7264
interface Window {
7365
versionInfoMap: Map<VersionInfo[0], VersionInfo[1]>
7466
}
7567
}
7668

69+
70+
import type { ReleasesResponse } from "./github-releases-api.ts";
71+
72+
const NULL = Symbol(`safePromise: null`);
73+
async function safePromise<P extends Promise<unknown>>(promise: P): Promise<[Awaited<P>, typeof NULL] | [typeof NULL, any]> {
74+
try {
75+
return [await promise, NULL];
76+
} catch (e) {
77+
return [NULL, e];
78+
}
79+
}
80+
81+
7782
async function populateVersionPicker() {
78-
const releases: ReleasesAPI[] = await fetch(
83+
const [response, responseError] = await safePromise(
84+
fetch(
7985
"https://api.github.com/repos/jindrapetrik/jpexs-decompiler/releases?per_page=20"
80-
)
81-
.then((res) => res.json())
82-
.catch(() => alert("Failed to fetch FFDEC versions"));
86+
) as Promise<ReleasesResponse>
87+
);
88+
89+
if(responseError !== NULL || response === NULL || !response.ok) {
90+
alert("Failed to fetch FFDEC versions");
91+
console.error("Error:", responseError, "Response:", response);
8392

84-
const versionInfo: VersionInfo[] = releases.map(release => {
93+
return;
94+
}
95+
96+
97+
const [releasesJson, jsonError] = await safePromise(
98+
(response as ReleasesResponse<200>).json()
99+
);
100+
101+
if(jsonError !== NULL || releasesJson === NULL) {
102+
alert("Failed to parse GitHub releases API response as JSON.")
103+
console.error("Error:", jsonError, "JSON:", releasesJson);
104+
105+
return;
106+
}
107+
108+
109+
const versionInfo: VersionInfo[] = releasesJson.map(release => {
85110
const { name, tag_name } = release;
86111
const downloadUrl = release.assets
87112
// Get only the universal zip. Technically "lib" is in the java doc
@@ -91,7 +116,7 @@ async function populateVersionPicker() {
91116
.at(0)!;
92117

93118
return [
94-
name,
119+
name!,
95120
{
96121
url: downloadUrl,
97122
slug: tag_name,
@@ -102,13 +127,14 @@ async function populateVersionPicker() {
102127

103128

104129
const versionSelect = document.querySelector("select")!;
130+
const versionSelectOptions: HTMLOptionElement[] = [];
105131

106-
versionSelect.append(...versionInfo.map(release => {
107-
const [name] = release;
108-
const elem = document.createElement("option");
132+
for (const [name] of versionInfo) {
133+
const option = document.createElement("option");
134+
option.textContent = name;
109135

110-
elem.innerText = name;
136+
versionSelectOptions.push(option);
137+
}
111138

112-
return elem;
113-
}));
139+
versionSelect.append(...versionSelectOptions);
114140
}

0 commit comments

Comments
 (0)