tangled
alpha
login
or
join now
jordanreger.com
/
htmlsky
0
fork
atom
An HTML-only Bluesky frontend
0
fork
atom
overview
issues
pulls
pipelines
move to js and update feeds
jordanreger.com
2 years ago
913c5036
f5cdb1df
+276
-282
6 changed files
expand all
collapse all
unified
split
actor.js
actor.ts
deno.json
facets.js
facets.ts
main.js
+229
actor.js
···
1
1
+
import { agent } from "./main.js";
2
2
+
import { getFacets, getDescriptionFacets } from "./facets.js";
3
3
+
4
4
+
const DateTimeFormat = new Intl.DateTimeFormat("en-US", {
5
5
+
dateStyle: "short",
6
6
+
timeStyle: "long",
7
7
+
timeZone: "UTC",
8
8
+
hour12: false,
9
9
+
});
10
10
+
11
11
+
export default class Actor {
12
12
+
uri;
13
13
+
actor;
14
14
+
15
15
+
constructor(uri) {
16
16
+
this.uri = uri;
17
17
+
this.actor = this.#get();
18
18
+
}
19
19
+
20
20
+
async #get() {
21
21
+
const { data: actor } = await agent.api.app.bsky.actor.getProfile({
22
22
+
actor: this.uri,
23
23
+
});
24
24
+
return actor;
25
25
+
}
26
26
+
27
27
+
async HTML(prevCursor) {
28
28
+
const actor = await this.actor;
29
29
+
30
30
+
actor.username = actor.displayName ? actor.displayName : actor.handle;
31
31
+
actor.avatar = actor.avatar ? actor.avatar : "/static/avatar.jpg";
32
32
+
33
33
+
return `
34
34
+
<head>
35
35
+
<meta name="color-scheme" content="light dark">
36
36
+
<meta name="viewport" content="width=device-width, initial-scale=1">
37
37
+
<title>${actor.username} (@${actor.handle}) — HTMLsky</title>
38
38
+
</head>
39
39
+
<table>
40
40
+
<tr>
41
41
+
<td valign="top" height="60" width="60">
42
42
+
<img src="${actor.avatar}" alt="${actor.username}'s avatar" height="60" width="60">
43
43
+
</td>
44
44
+
<td>
45
45
+
<h1>
46
46
+
<span>${actor.username}</span><br>
47
47
+
<small><small><small>@${actor.handle}</small></small></small>
48
48
+
</h1>
49
49
+
</td>
50
50
+
</tr>
51
51
+
${
52
52
+
actor.description
53
53
+
? `<tr>
54
54
+
<td colspan="2">
55
55
+
<p>${await getDescriptionFacets(actor.description).then((res) => res.replaceAll("\n", "<br>"))}</p>
56
56
+
</td>
57
57
+
</tr>
58
58
+
<tr>
59
59
+
<td colspan="2"> </td>
60
60
+
</tr>`
61
61
+
: ``
62
62
+
}
63
63
+
<tr>
64
64
+
<td colspan="2">
65
65
+
<a href="./followers/"><b>${actor.followersCount}</b> followers</a>
66
66
+
<a href="./follows/"><b>${actor.followsCount}</b> following</a>
67
67
+
<b>${actor.postsCount}</b> posts
68
68
+
</td>
69
69
+
</tr>
70
70
+
</table>
71
71
+
<hr>
72
72
+
${prevCursor ? await this.Feed(prevCursor) : await this.Feed()}
73
73
+
`;
74
74
+
}
75
75
+
76
76
+
async Feed(prevCursor) {
77
77
+
const actor = await this.actor;
78
78
+
let options = { actor: actor.did };
79
79
+
if (prevCursor) {
80
80
+
options = { actor: actor.did, cursor: prevCursor };
81
81
+
}
82
82
+
const { data } = await agent.api.app.bsky.feed.getAuthorFeed(options);
83
83
+
const { feed, cursor } = data;
84
84
+
85
85
+
const feedList = [];
86
86
+
87
87
+
feed.forEach(async (post) => {
88
88
+
if (post.reply) {
89
89
+
const reply = post.reply.parent ? post.reply.parent : post.reply.root;
90
90
+
const author = reply.author;
91
91
+
if (reply.notFound || reply.blocked) {
92
92
+
feedList.push(`
93
93
+
<tr>
94
94
+
<td>
95
95
+
<table>
96
96
+
<tr><td>
97
97
+
Post was deleted.
98
98
+
</td></tr>
99
99
+
</table>
100
100
+
</td>
101
101
+
</tr>
102
102
+
`);
103
103
+
} else {
104
104
+
feedList.push(`
105
105
+
<tr>
106
106
+
<td>
107
107
+
<table>
108
108
+
<tr><td><b>${reply.author.displayName ? reply.author.displayName : reply.author.handle}</b> (<a href="/profile/${reply.author.handle !== "handle.invalid" ? reply.author.handle : reply.author.did}/">@${reply.author.handle}</a>) · ${DateTimeFormat.format(new Date(reply.record.createdAt))}</td></tr>
109
109
+
<tr><td>${/*await getFacets(*/reply.record.text/*).then((res) => res.replaceAll("\n", "<br>"))*/}</td></tr>
110
110
+
<tr><td><b>${reply.replyCount}</b> replies · <b>${reply.repostCount}</b> reposts · <b>${reply.likeCount}</b> likes</td></tr>
111
111
+
</table>
112
112
+
</td>
113
113
+
</tr>
114
114
+
`);
115
115
+
}
116
116
+
}
117
117
+
118
118
+
const record = post.post.record;
119
119
+
const author = post.post.author;
120
120
+
121
121
+
feedList.push(`
122
122
+
<tr>
123
123
+
<td>
124
124
+
${post.reply ? `<blockquote>` : ``}
125
125
+
<table>
126
126
+
${actor.did !== author.did ? `<tr><td><i>Reposted by ${actor.displayName ? actor.displayName : actor.handle}</i></td></tr>` : ``}
127
127
+
<tr><td><b>${author.displayName ? author.displayName : author.handle}</b> (<a href="/profile/${author.handle !== "handle.invalid" ? author.handle : author.did }/">@${author.handle}</a>) · ${DateTimeFormat.format(new Date(record.createdAt))}</td></tr>
128
128
+
<tr><td><p>${/*await getFacets(*/record.text/*).then((res) => res.replaceAll("\n", "<br>"))*/}</p></td></tr>
129
129
+
<tr><td><b>${post.post.replyCount}</b> replies · <b>${post.post.repostCount}</b> reposts · <b>${post.post.likeCount}</b> likes</td></tr>
130
130
+
</table>
131
131
+
${post.reply ? `</blockquote><hr>` : `<hr>`}
132
132
+
</td>
133
133
+
</tr>
134
134
+
`);
135
135
+
});
136
136
+
137
137
+
if (cursor) {
138
138
+
feedList.push(`
139
139
+
<tr>
140
140
+
<td><br><a href="?cursor=${cursor}">Next page</a></td>
141
141
+
</tr>
142
142
+
`);
143
143
+
}
144
144
+
145
145
+
return `
146
146
+
<table width="100%">
147
147
+
${feedList.join("")}
148
148
+
</table>
149
149
+
`;
150
150
+
}
151
151
+
152
152
+
async Followers(prevCursor) {
153
153
+
const actor = await this.actor;
154
154
+
let options = { actor: actor.did };
155
155
+
if (prevCursor) {
156
156
+
options = { actor: actor.did, cursor: prevCursor };
157
157
+
}
158
158
+
159
159
+
const { data } = await agent.api.app.bsky.graph.getFollowers(options);
160
160
+
const { followers, cursor } = data;
161
161
+
162
162
+
const followersList = [];
163
163
+
followers.forEach((follower) => {
164
164
+
followersList.push(`
165
165
+
<tr>
166
166
+
<td><b>${follower.displayName ? follower.displayName : follower.handle}</b> (<a href="/profile/${follower.handle !== "handle.invalid" ? follower.handle : follower.did}/">@${follower.handle}</a>)</td>
167
167
+
</tr>`);
168
168
+
});
169
169
+
170
170
+
if (cursor) {
171
171
+
followersList.push(`
172
172
+
<tr>
173
173
+
<td><br><a href="?cursor=${cursor}">Next page</a></td>
174
174
+
</tr>
175
175
+
`);
176
176
+
}
177
177
+
178
178
+
return `
179
179
+
<head>
180
180
+
<meta name="color-scheme" content="light dark">
181
181
+
<meta name="viewport" content="width=device-width, initial-scale=1">
182
182
+
<title>People following @${actor.handle} — HTMLsky</title>
183
183
+
</head>
184
184
+
<p><a href="..">Back</a></p>
185
185
+
<table>
186
186
+
${followersList.join("")}
187
187
+
</table>
188
188
+
`;
189
189
+
}
190
190
+
191
191
+
async Follows(prevCursor) {
192
192
+
const actor = await this.actor;
193
193
+
let options = { actor: actor.did };
194
194
+
if (prevCursor) {
195
195
+
options = { actor: actor.did, cursor: prevCursor };
196
196
+
}
197
197
+
198
198
+
const { data } = await agent.api.app.bsky.graph.getFollows(options);
199
199
+
const { follows, cursor } = data;
200
200
+
201
201
+
const followsList = [];
202
202
+
follows.forEach((follow) => {
203
203
+
followsList.push(`
204
204
+
<tr>
205
205
+
<td><b>${follow.displayName ? follow.displayName : follow.handle}</b> (<a href="/profile/${follow.handle !== "handle.invalid" ? follow.handle : follow.did}/">@${follow.handle}</a>)</td>
206
206
+
</tr>`);
207
207
+
});
208
208
+
209
209
+
if (cursor) {
210
210
+
followsList.push(`
211
211
+
<tr>
212
212
+
<td><br><a href="?cursor=${cursor}">Next page</a></td>
213
213
+
</tr>
214
214
+
`);
215
215
+
}
216
216
+
217
217
+
return `
218
218
+
<head>
219
219
+
<meta name="color-scheme" content="light dark">
220
220
+
<meta name="viewport" content="width=device-width, initial-scale=1">
221
221
+
<title>People followed by @${actor.handle} — HTMLsky</title>
222
222
+
</head>
223
223
+
<p><a href="..">Back</a></p>
224
224
+
<table>
225
225
+
${followsList.join("")}
226
226
+
</table>
227
227
+
`;
228
228
+
}
229
229
+
}
-246
actor.ts
···
1
1
-
import {
2
2
-
AppBskyActorDefs,
3
3
-
AppBskyFeedGetAuthorFeed,
4
4
-
AppBskyFeedPost,
5
5
-
AppBskyGraphGetFollowers,
6
6
-
} from "npm:@atproto/api";
7
7
-
8
8
-
import { agent } from "./main.ts";
9
9
-
import { getDescriptionFacets } from "./facets.ts";
10
10
-
11
11
-
const DateTimeFormat = new Intl.DateTimeFormat("en-US", {
12
12
-
dateStyle: "short",
13
13
-
timeStyle: "long",
14
14
-
timeZone: "UTC",
15
15
-
hour12: false,
16
16
-
});
17
17
-
18
18
-
export default class Actor {
19
19
-
uri: string;
20
20
-
actor: Promise<AppBskyActorDefs.ProfileViewDetailed>;
21
21
-
22
22
-
constructor(uri: string) {
23
23
-
this.uri = uri;
24
24
-
this.actor = this.#get();
25
25
-
}
26
26
-
27
27
-
async #get() {
28
28
-
const { data: actor } = await agent.api.app.bsky.actor.getProfile({
29
29
-
actor: this.uri,
30
30
-
});
31
31
-
return actor;
32
32
-
}
33
33
-
34
34
-
async Raw(): Promise<string> {
35
35
-
const actor = await this.actor;
36
36
-
37
37
-
return JSON.stringify(actor, null, 2);
38
38
-
}
39
39
-
40
40
-
async HTML(prevCursor?: string): Promise<string> {
41
41
-
const actor = await this.actor;
42
42
-
43
43
-
actor.username = actor.displayName ? actor.displayName : actor.handle;
44
44
-
actor.avatar = actor.avatar ? actor.avatar : "/static/avatar.jpg";
45
45
-
46
46
-
return `
47
47
-
<head>
48
48
-
<meta name="color-scheme" content="light dark">
49
49
-
<meta name="viewport" content="width=device-width, initial-scale=1">
50
50
-
<title>${actor.username} (@${actor.handle}) — HTMLsky</title>
51
51
-
</head>
52
52
-
<table>
53
53
-
<tr>
54
54
-
<td valign="top" height="60" width="60">
55
55
-
<img src="${actor.avatar}" alt="${actor.username}'s avatar" height="60" width="60">
56
56
-
</td>
57
57
-
<td>
58
58
-
<h1>
59
59
-
<span>${actor.username}</span><br>
60
60
-
<small><small><small>@${actor.handle}</small></small></small>
61
61
-
</h1>
62
62
-
</td>
63
63
-
</tr>
64
64
-
${
65
65
-
actor.description
66
66
-
? `<tr>
67
67
-
<td colspan="2">
68
68
-
<p>${await getDescriptionFacets(actor.description).then((res) =>
69
69
-
res.replaceAll("\n", "<br>")
70
70
-
)}</p>
71
71
-
</td>
72
72
-
</tr>
73
73
-
<tr>
74
74
-
<td colspan="2"> </td>
75
75
-
</tr>`
76
76
-
: ``
77
77
-
}
78
78
-
<tr>
79
79
-
<td colspan="2">
80
80
-
<a href="./followers/"><b>${actor.followersCount}</b> followers</a>
81
81
-
<a href="./follows/"><b>${actor.followsCount}</b> following</a>
82
82
-
<b>${actor.postsCount}</b> posts
83
83
-
</td>
84
84
-
</tr>
85
85
-
</table>
86
86
-
<hr>
87
87
-
${prevCursor ? await this.Feed(prevCursor) : await this.Feed()}
88
88
-
`;
89
89
-
}
90
90
-
91
91
-
async Feed(prevCursor?: string): Promise<string> {
92
92
-
const actor = await this.actor;
93
93
-
let options: AppBskyFeedGetAuthorFeed.QueryParams = { actor: actor.did };
94
94
-
if (prevCursor) {
95
95
-
options = { actor: actor.did, cursor: prevCursor };
96
96
-
}
97
97
-
const { data } = await agent.api.app.bsky.feed.getAuthorFeed(options);
98
98
-
const { feed, cursor } = data;
99
99
-
100
100
-
const feedList: string[] = [];
101
101
-
feed.forEach((post) => {
102
102
-
const postAuthor = post.post.author,
103
103
-
reply = post.reply?.root,
104
104
-
record: AppBskyFeedPost.Record = post.post.record;
105
105
-
let replyAuthor: AppBskyActorDefs.ProfileView | undefined;
106
106
-
if (reply) replyAuthor = reply.author;
107
107
-
else replyAuthor = undefined;
108
108
-
console.log(post);
109
109
-
feedList.push(`
110
110
-
<tr>
111
111
-
<td>
112
112
-
${
113
113
-
reply
114
114
-
? `
115
115
-
<table>
116
116
-
<tr><td><b>${
117
117
-
replyAuthor.displayName
118
118
-
? replyAuthor.displayName
119
119
-
: replyAuthor.handle
120
120
-
}</b> (@<a href="/profile/${
121
121
-
replyAuthor.handle !== "handle.invalid"
122
122
-
? replyAuthor.handle
123
123
-
: replyAuthor.did
124
124
-
}">${replyAuthor.handle}</a>)</td></tr>
125
125
-
<tr><td>${reply.record.text}</td></tr>
126
126
-
<tr><td>${DateTimeFormat.format(new Date(reply.record.createdAt))}</td></tr>
127
127
-
<tr><td><br></td></tr>
128
128
-
</table>
129
129
-
<blockquote>
130
130
-
`
131
131
-
: ""
132
132
-
}
133
133
-
<table>
134
134
-
${
135
135
-
actor.did !== postAuthor.did
136
136
-
? `<tr><td><i>Reposted by ${
137
137
-
actor.displayName ? actor.displayName : actor.handle
138
138
-
}</i></td></tr>`
139
139
-
: ``
140
140
-
}
141
141
-
<tr><td><b>${
142
142
-
postAuthor.displayName ? postAuthor.displayName : postAuthor.handle
143
143
-
}</b> (@<a href="/profile/${
144
144
-
postAuthor.handle !== "handle.invalid"
145
145
-
? postAuthor.handle
146
146
-
: postAuthor.did
147
147
-
}">${postAuthor.handle}</a>)</td></tr>
148
148
-
<tr><td>${record.text}</td></tr>
149
149
-
<tr><td>${DateTimeFormat.format(new Date(record.createdAt))}</td></tr>
150
150
-
<tr><td><br></td></tr>
151
151
-
</table>
152
152
-
${post.reply ? "</blockquote>" : ""}
153
153
-
</td>
154
154
-
</tr>
155
155
-
`);
156
156
-
});
157
157
-
158
158
-
if (cursor) {
159
159
-
feedList.push(`
160
160
-
<tr>
161
161
-
<td><br><a href="?cursor=${cursor}">Next page</a></td>
162
162
-
</tr>
163
163
-
`);
164
164
-
}
165
165
-
166
166
-
return `
167
167
-
<table>
168
168
-
${feedList.join("")}
169
169
-
</table>
170
170
-
`;
171
171
-
}
172
172
-
173
173
-
async Followers(prevCursor?: string): Promise<string> {
174
174
-
const actor = await this.actor;
175
175
-
let options: AppBskyGraphGetFollowers.QueryParams = { actor: actor.did };
176
176
-
if (prevCursor) {
177
177
-
options = { actor: actor.did, cursor: prevCursor };
178
178
-
}
179
179
-
180
180
-
const { data } = await agent.api.app.bsky.graph.getFollowers(options);
181
181
-
const { followers, cursor } = data;
182
182
-
183
183
-
const followersList: string[] = [];
184
184
-
followers.forEach((follower) => {
185
185
-
followersList.push(`
186
186
-
<tr>
187
187
-
<td><b>${
188
188
-
follower.displayName ? follower.displayName : follower.handle
189
189
-
}</b> (@${follower.handle})</td>
190
190
-
</tr>`);
191
191
-
});
192
192
-
193
193
-
if (cursor) {
194
194
-
followersList.push(`
195
195
-
<tr>
196
196
-
<td><br><a href="?cursor=${cursor}">Next page</a></td>
197
197
-
</tr>
198
198
-
`);
199
199
-
}
200
200
-
201
201
-
return `
202
202
-
<p><a href="..">Back</a></p>
203
203
-
<table>
204
204
-
${followersList.join("")}
205
205
-
</table>
206
206
-
`;
207
207
-
}
208
208
-
209
209
-
async Follows(prevCursor?: string): Promise<string> {
210
210
-
const actor = await this.actor;
211
211
-
let options: AppBskyGraphGetFollowers.QueryParams = { actor: actor.did };
212
212
-
if (prevCursor) {
213
213
-
options = { actor: actor.did, cursor: prevCursor };
214
214
-
}
215
215
-
216
216
-
const { data } = await agent.api.app.bsky.graph.getFollows(options);
217
217
-
const { follows, cursor } = data;
218
218
-
219
219
-
const followsList: string[] = [];
220
220
-
follows.forEach((follow) => {
221
221
-
followsList.push(`
222
222
-
<tr>
223
223
-
<td><b>${
224
224
-
follow.displayName ? follow.displayName : follow.handle
225
225
-
}</b> (<a href="/profile/${
226
226
-
follow.handle !== "handle.invalid" ? follow.handle : follow.did
227
227
-
}">@${follow.handle}</a>)</td>
228
228
-
</tr>`);
229
229
-
});
230
230
-
231
231
-
if (cursor) {
232
232
-
followsList.push(`
233
233
-
<tr>
234
234
-
<td><br><a href="?cursor=${cursor}">Next page</a></td>
235
235
-
</tr>
236
236
-
`);
237
237
-
}
238
238
-
239
239
-
return `
240
240
-
<p><a href="..">Back</a></p>
241
241
-
<table>
242
242
-
${followsList.join("")}
243
243
-
</table>
244
244
-
`;
245
245
-
}
246
246
-
}
+1
-5
deno.json
···
1
1
{
2
2
"tasks": {
3
3
-
"dev": "deno task clean && deno run -A --watch main.ts",
3
3
+
"dev": "deno run -A --watch main.js",
4
4
"clean": "deno fmt && deno lint"
5
5
-
},
6
6
-
"compilerOptions": {
7
7
-
"jsx": "react-jsx",
8
8
-
"jsxImportSource": "npm:preact@10.22.0"
9
5
}
10
6
}
+40
facets.js
···
1
1
+
import { RichText } from "npm:@atproto/api";
2
2
+
import { agent } from "./main.js";
3
3
+
4
4
+
export async function getFacets(text) {
5
5
+
const rt = new RichText({ text: text });
6
6
+
await rt.detectFacets(agent);
7
7
+
8
8
+
let res = "";
9
9
+
10
10
+
for (const segment of rt.segments()) {
11
11
+
if (segment.isLink()) {
12
12
+
res += `<a href="${segment.link?.uri}">${segment.text}</a>`;
13
13
+
} else if (segment.isMention()) {
14
14
+
res += `<a href="/profile/${segment.mention?.did}/">${segment.text}</a>`;
15
15
+
} else {
16
16
+
res += segment.text;
17
17
+
}
18
18
+
}
19
19
+
20
20
+
return res;
21
21
+
}
22
22
+
23
23
+
export async function getDescriptionFacets(text) {
24
24
+
const rt = new RichText({ text: text });
25
25
+
await rt.detectFacets(agent);
26
26
+
27
27
+
let res = "";
28
28
+
29
29
+
for (const segment of rt.segments()) {
30
30
+
if (segment.isLink()) {
31
31
+
res += `<a href="${segment.link.uri}">${segment.text}</a>`;
32
32
+
} else if (segment.isMention()) {
33
33
+
res += `<a href="/profile/${segment.mention.did}/">${segment.text}</a>`;
34
34
+
} else {
35
35
+
res += segment.text;
36
36
+
}
37
37
+
}
38
38
+
39
39
+
return res;
40
40
+
}
-25
facets.ts
···
1
1
-
import { RichText } from "npm:@atproto/api";
2
2
-
import { agent } from "./main.ts";
3
3
-
4
4
-
export async function getDescriptionFacets(
5
5
-
description: string,
6
6
-
): Promise<string> {
7
7
-
const rt = new RichText({ text: description });
8
8
-
await rt.detectFacets(agent);
9
9
-
10
10
-
let descriptionWithFacets = "";
11
11
-
12
12
-
for (const segment of rt.segments()) {
13
13
-
if (segment.isLink()) {
14
14
-
descriptionWithFacets +=
15
15
-
`<a href="${segment.link?.uri}">${segment.text}</a>`;
16
16
-
} else if (segment.isMention()) {
17
17
-
descriptionWithFacets +=
18
18
-
`<a href="/profile/${segment.mention?.did}">${segment.text}</a>`;
19
19
-
} else {
20
20
-
descriptionWithFacets += segment.text;
21
21
-
}
22
22
-
}
23
23
-
24
24
-
return descriptionWithFacets;
25
25
-
}
+6
-6
main.ts
main.js
···
1
1
import { AtpAgent } from "npm:@atproto/api";
2
2
import { serveDir, serveFile } from "jsr:@std/http/file-server";
3
3
4
4
-
import Actor from "./actor.ts";
4
4
+
import Actor from "./actor.js";
5
5
6
6
export const agent = new AtpAgent({ service: "https://public.api.bsky.app" });
7
7
···
33
33
const profilePattern = new URLPattern({ pathname: "/profile/:actor/" });
34
34
if (profilePattern.test(url)) {
35
35
const actorName = profilePattern.exec(url)?.pathname.groups.actor;
36
36
-
const actor = new Actor(actorName!);
36
36
+
const actor = new Actor(actorName);
37
37
const cursor = query.get("cursor");
38
38
if (cursor) return new Response(await actor.HTML(cursor), html_headers);
39
39
else return new Response(await actor.HTML(), html_headers);
···
45
45
});
46
46
if (rawProfilePattern.test(url)) {
47
47
const actorName = rawProfilePattern.exec(url)?.pathname.groups.actor;
48
48
-
const actor = new Actor(actorName!);
48
48
+
const actor = new Actor(actorName);
49
49
50
50
return new Response(await actor.Raw(), json_headers);
51
51
}
···
57
57
if (pageProfilePattern.test(url)) {
58
58
const actorName = pageProfilePattern.exec(url)?.pathname.groups.actor;
59
59
const pageName = pageProfilePattern.exec(url)?.pathname.groups.page;
60
60
-
const actor = new Actor(actorName!);
60
60
+
const actor = new Actor(actorName);
61
61
62
62
if (pageName === "followers") {
63
63
const cursor = query.get("cursor");
···
85
85
);
86
86
});
87
87
88
88
-
const html_headers: ResponseInit = {
88
88
+
const html_headers = {
89
89
"headers": {
90
90
"Content-Type": "text/html;charset=utf-8",
91
91
},
92
92
};
93
93
-
const json_headers: ResponseInit = {
93
93
+
const json_headers = {
94
94
"headers": {
95
95
"Content-Type": "application/json;charset=utf-8",
96
96
},