@fedify/botkit 0.5.0-dev.210 → 0.5.0-dev.226

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/bot-group.test.d.ts +2 -0
  2. package/dist/bot-group.test.js +220 -0
  3. package/dist/bot-group.test.js.map +1 -0
  4. package/dist/bot-impl.d.ts +132 -13
  5. package/dist/bot-impl.d.ts.map +1 -1
  6. package/dist/bot-impl.js +400 -178
  7. package/dist/bot-impl.js.map +1 -1
  8. package/dist/bot-impl.test.js +214 -76
  9. package/dist/bot-impl.test.js.map +1 -1
  10. package/dist/bot.d.ts +94 -48
  11. package/dist/bot.d.ts.map +1 -1
  12. package/dist/bot.js +2 -104
  13. package/dist/bot.js.map +1 -1
  14. package/dist/bot.test.js +59 -0
  15. package/dist/bot.test.js.map +1 -1
  16. package/dist/components/FollowButton.d.ts +3 -1
  17. package/dist/components/FollowButton.d.ts.map +1 -1
  18. package/dist/components/FollowButton.js +2 -2
  19. package/dist/components/FollowButton.js.map +1 -1
  20. package/dist/components/Layout.js +1 -1
  21. package/dist/components/Layout.js.map +1 -1
  22. package/dist/components/Message.d.ts +2 -2
  23. package/dist/deno.js +3 -13
  24. package/dist/deno.js.map +1 -1
  25. package/dist/follow-impl.test.js +3 -3
  26. package/dist/follow-impl.test.js.map +1 -1
  27. package/dist/instance-impl.d.ts +158 -0
  28. package/dist/instance-impl.d.ts.map +1 -0
  29. package/dist/instance-impl.js +603 -0
  30. package/dist/instance-impl.js.map +1 -0
  31. package/dist/instance-impl.test.d.ts +2 -0
  32. package/dist/instance-impl.test.js +103 -0
  33. package/dist/instance-impl.test.js.map +1 -0
  34. package/dist/instance-multi.test.d.ts +2 -0
  35. package/dist/instance-multi.test.js +151 -0
  36. package/dist/instance-multi.test.js.map +1 -0
  37. package/dist/instance-routing.test.d.ts +2 -0
  38. package/dist/instance-routing.test.js +367 -0
  39. package/dist/instance-routing.test.js.map +1 -0
  40. package/dist/instance.d.ts +318 -0
  41. package/dist/instance.d.ts.map +1 -0
  42. package/dist/instance.js +51 -0
  43. package/dist/instance.js.map +1 -0
  44. package/dist/message-impl.d.ts.map +1 -1
  45. package/dist/message-impl.js +17 -10
  46. package/dist/message-impl.js.map +1 -1
  47. package/dist/message-impl.test.js +43 -9
  48. package/dist/message-impl.test.js.map +1 -1
  49. package/dist/mod.d.ts +5 -3
  50. package/dist/mod.js +4 -2
  51. package/dist/pages.d.ts +10 -1
  52. package/dist/pages.d.ts.map +1 -1
  53. package/dist/pages.js +112 -41
  54. package/dist/pages.js.map +1 -1
  55. package/dist/pages.test.d.ts +2 -0
  56. package/dist/pages.test.js +170 -0
  57. package/dist/pages.test.js.map +1 -0
  58. package/dist/repository.d.ts +385 -138
  59. package/dist/repository.d.ts.map +1 -1
  60. package/dist/repository.js +595 -223
  61. package/dist/repository.js.map +1 -1
  62. package/dist/repository.test.js +564 -136
  63. package/dist/repository.test.js.map +1 -1
  64. package/dist/session-impl.d.ts.map +1 -1
  65. package/dist/session-impl.js +13 -4
  66. package/dist/session-impl.js.map +1 -1
  67. package/dist/session-impl.test.d.ts.map +1 -1
  68. package/dist/session-impl.test.js +9 -9
  69. package/dist/session-impl.test.js.map +1 -1
  70. package/dist/session.d.ts +8 -3
  71. package/dist/session.d.ts.map +1 -1
  72. package/dist/text.d.ts.map +1 -1
  73. package/dist/text.js +27 -10
  74. package/dist/text.js.map +1 -1
  75. package/dist/text.test.js +37 -2
  76. package/dist/text.test.js.map +1 -1
  77. package/dist/uri.d.ts +46 -0
  78. package/dist/uri.d.ts.map +1 -0
  79. package/dist/uri.js +64 -0
  80. package/dist/uri.js.map +1 -0
  81. package/dist/uri.test.d.ts +2 -0
  82. package/dist/uri.test.js +93 -0
  83. package/dist/uri.test.js.map +1 -0
  84. package/package.json +6 -2
@@ -1 +1 @@
1
- {"version":3,"file":"pages.d.ts","names":[],"sources":["../src/pages.tsx"],"sourcesContent":[],"mappings":";;;;;;;UAwCiB,QAAA;gBACD;;;UAIC,GAAA;qBACI;;AANJ,cASJ,GATY,EAST,IARA,CAQA,GARO,EAQP,WAAA,CAAA,WAAA,EARO,GAAA,CAAA;AAIvB"}
1
+ {"version":3,"file":"pages.d.ts","names":[],"sources":["../src/pages.tsx"],"sourcesContent":[],"mappings":";;;;;;;;UA0CiB,QAAA;gBACD;;;UAIC,GAAA;qBACI;;AANJ,UASA,gBAAA,CARM;EAIN,SAAG,QAAA,EAKC,YAJQ,CAAA,OAAA,CAAA;EAGZ,SAAA,WAAgB,EAAA,OAAA;AAKjC;AAOa,UAPI,WAAA,CAOiB;EAAA,SAAA,QAAA,EANb,gBAMa;;AAAlB,cAAH,GAAG,EAAA,IAAA,CAAA,GAAA,EAAA,WAAA,CAAA,WAAA,EAAA,GAAA,CAAA;AAAA,cAyhBH,QAzhBG,EAyhBK,IAzhBL,CAyhBK,WAzhBL,EAyhBK,WAAA,CAAA,WAAA,EAzhBL,GAAA,CAAA;AAAA"}
package/dist/pages.js CHANGED
@@ -15,9 +15,10 @@ import { jsx, jsxs } from "hono/jsx/jsx-runtime";
15
15
 
16
16
  //#region src/pages.tsx
17
17
  const app = new Hono();
18
- app.get("/", async (c) => {
19
- const { bot } = c.env;
20
- const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);
18
+ app.get("/", (c) => profilePage(c, c.env.bot, c.env.contextData, ""));
19
+ async function profilePage(c, bot, contextData, base) {
20
+ const home = base === "" ? "/" : base;
21
+ const ctx = bot.federation.createContext(c.req.raw, contextData);
21
22
  const session = bot.getSession(ctx);
22
23
  const url = new URL(c.req.url);
23
24
  const handle = `@${bot.username}@${url.host}`;
@@ -40,10 +41,10 @@ app.get("/", async (c) => {
40
41
  const offset = c.req.query("offset");
41
42
  const { posts: messages, nextPost } = await getPosts(bot, ctx, offset ? { offset: Temporal.Instant.from(offset) } : {});
42
43
  const activityLink = ctx.getActorUri(bot.identifier);
43
- const feedLink = new URL("/feed.xml", url);
44
+ const feedLink = new URL(`${base}/feed.xml`, url);
44
45
  let nextLink;
45
46
  if (nextPost?.published != null) {
46
- nextLink = new URL("/", url);
47
+ nextLink = new URL(home, url);
47
48
  nextLink.searchParams.set("offset", nextPost.published.toString());
48
49
  }
49
50
  return c.html(/* @__PURE__ */ jsxs(Layout, {
@@ -70,7 +71,7 @@ app.get("/", async (c) => {
70
71
  style: "float: left; margin-right: 1em; height: 72;"
71
72
  }),
72
73
  /* @__PURE__ */ jsx("h1", { children: /* @__PURE__ */ jsx("a", {
73
- href: "/",
74
+ href: home,
74
75
  children: bot.name ?? bot.username
75
76
  }) }),
76
77
  /* @__PURE__ */ jsxs("p", { children: [
@@ -81,7 +82,7 @@ app.get("/", async (c) => {
81
82
  " ·",
82
83
  " ",
83
84
  /* @__PURE__ */ jsx("a", {
84
- href: "/feed.xml",
85
+ href: `${base}/feed.xml`,
85
86
  rel: "alternate",
86
87
  type: "application/atom+xml",
87
88
  title: "Atom feed",
@@ -101,7 +102,7 @@ app.get("/", async (c) => {
101
102
  "·",
102
103
  " ",
103
104
  /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx("a", {
104
- href: "/followers",
105
+ href: `${base}/followers`,
105
106
  children: followersCount === 1 ? `1 follower` : `${followersCount.toLocaleString("en")} followers`
106
107
  }) }),
107
108
  " ",
@@ -110,7 +111,10 @@ app.get("/", async (c) => {
110
111
  /* @__PURE__ */ jsx("span", { children: postsCount === 1 ? `1 post` : `${postsCount.toLocaleString("en")} posts` }),
111
112
  " ",
112
113
  "· ",
113
- /* @__PURE__ */ jsx(FollowButton, { bot })
114
+ /* @__PURE__ */ jsx(FollowButton, {
115
+ bot,
116
+ action: `${base}/follow`
117
+ })
114
118
  ] })
115
119
  ] }),
116
120
  summary && /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: summary } }),
@@ -141,16 +145,17 @@ app.get("/", async (c) => {
141
145
  })
142
146
  ]
143
147
  }), { headers: { Link: `<${activityLink.href}>; rel="alternate"; type="application/activity+json", <${feedLink.href}>; rel="alternate"; type="application/atom+xml"` + (nextLink ? `, <${nextLink.href}>; rel="next"; type="text/html"` : "") } });
144
- });
145
- app.get("/followers", async (c) => {
146
- const { bot } = c.env;
147
- const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);
148
+ }
149
+ app.get("/followers", (c) => followersPage(c, c.env.bot, c.env.contextData, ""));
150
+ async function followersPage(c, bot, contextData, base) {
151
+ const home = base === "" ? "/" : base;
152
+ const ctx = bot.federation.createContext(c.req.raw, contextData);
148
153
  const session = bot.getSession(ctx);
149
154
  const followersCount = await bot.repository.countFollowers();
150
155
  const followers = await Array.fromAsync(bot.repository.getFollowers());
151
156
  const url = new URL(c.req.url);
152
157
  const activityLink = ctx.getActorUri(bot.identifier);
153
- const feedLink = new URL("/feed.xml", url);
158
+ const feedLink = new URL(`${base}/feed.xml`, url);
154
159
  return c.html(/* @__PURE__ */ jsxs(Layout, {
155
160
  bot,
156
161
  host: url.host,
@@ -160,7 +165,7 @@ app.get("/followers", async (c) => {
160
165
  class: "container",
161
166
  children: /* @__PURE__ */ jsxs("h1", { children: [
162
167
  /* @__PURE__ */ jsx("a", {
163
- href: "/",
168
+ href: home,
164
169
  children: "←"
165
170
  }),
166
171
  " ",
@@ -174,12 +179,13 @@ app.get("/followers", async (c) => {
174
179
  }, follower.id?.href ?? index))
175
180
  })]
176
181
  }));
177
- });
178
- app.get("/tags/:hashtag", async (c) => {
182
+ }
183
+ app.get("/tags/:hashtag", (c) => hashtagPage(c, c.env.bot, c.env.contextData, ""));
184
+ async function hashtagPage(c, bot, contextData, base) {
179
185
  const hashtag = c.req.param("hashtag");
180
- const { bot } = c.env;
186
+ if (hashtag == null) return c.notFound();
181
187
  const url = new URL(c.req.url);
182
- const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);
188
+ const ctx = bot.federation.createContext(c.req.raw, contextData);
183
189
  const session = bot.getSession(ctx);
184
190
  const offset = c.req.query("offset");
185
191
  const { posts, nextPost } = await getPosts(bot, ctx, {
@@ -188,7 +194,7 @@ app.get("/tags/:hashtag", async (c) => {
188
194
  });
189
195
  let nextLink;
190
196
  if (nextPost?.published != null) {
191
- nextLink = new URL(`/tags/${encodeURIComponent(hashtag)}`, url);
197
+ nextLink = new URL(`${base}/tags/${encodeURIComponent(hashtag)}`, url);
192
198
  nextLink.searchParams.set("offset", nextPost.published.toString());
193
199
  }
194
200
  return c.html(/* @__PURE__ */ jsxs(Layout, {
@@ -220,19 +226,23 @@ app.get("/tags/:hashtag", async (c) => {
220
226
  })
221
227
  ]
222
228
  }), { headers: nextLink == null ? {} : { Link: `<${nextLink.href}>; rel="next"; type="text/html"` } });
223
- });
224
- app.get("/message/:id", async (c) => {
229
+ }
230
+ app.get("/message/:id", (c) => messagePage(c, c.env.bot, c.env.contextData, ""));
231
+ async function messagePage(c, bot, contextData, base) {
225
232
  const id = c.req.param("id");
226
- const { bot } = c.env;
233
+ if (id == null) return c.notFound();
227
234
  const url = new URL(c.req.url);
228
- const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);
235
+ const ctx = bot.federation.createContext(c.req.raw, contextData);
229
236
  const session = bot.getSession(ctx);
230
237
  const post = await bot.repository.getMessage(id);
231
238
  if (post == null || !isPublic(post)) return c.notFound();
232
239
  const message = await post.getObject(ctx);
233
240
  if (message == null || !isMessageObject(message)) return c.notFound();
234
- const activityLink = ctx.getObjectUri(getMessageClass(message), { id });
235
- const feedLink = new URL("/feed.xml", url);
241
+ const activityLink = ctx.getObjectUri(getMessageClass(message), {
242
+ identifier: bot.identifier,
243
+ id
244
+ });
245
+ const feedLink = new URL(`${base}/feed.xml`, url);
236
246
  let title = message.name;
237
247
  if (title == null) {
238
248
  title = message.summary ?? message.content;
@@ -252,16 +262,17 @@ app.get("/message/:id", async (c) => {
252
262
  })
253
263
  })
254
264
  }), { headers: { Link: `<${activityLink.href}>; rel="alternate"; type="application/activity+json", <${feedLink.href}>; rel="alternate"; type="application/atom+xml"` } });
255
- });
256
- app.get("/feed.xml", async (c) => {
257
- const { bot } = c.env;
265
+ }
266
+ app.get("/feed.xml", (c) => feedPage(c, c.env.bot, c.env.contextData, ""));
267
+ async function feedPage(c, bot, contextData, base) {
268
+ const home = base === "" ? "/" : base;
258
269
  const url = new URL(c.req.url);
259
- const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);
270
+ const ctx = bot.federation.createContext(c.req.raw, contextData);
260
271
  const session = bot.getSession(ctx);
261
272
  const { posts } = await getPosts(bot, ctx, { window: 30 });
262
273
  const botName = bot.name ?? bot.username;
263
- const canonicalUrl = new URL("/feed.xml", url);
264
- const profileUrl = new URL("/", url);
274
+ const canonicalUrl = new URL(`${base}/feed.xml`, url);
275
+ const profileUrl = new URL(home, url);
265
276
  const actorUrl = ctx.getActorUri(bot.identifier);
266
277
  c.header("Link", `<${actorUrl.href}>; rel="alternate"; type="application/activity+json", <${profileUrl.href}>; rel="alternate"; type="text/html"`);
267
278
  const response = await c.render(/* @__PURE__ */ jsxs("feed", {
@@ -340,10 +351,11 @@ app.get("/feed.xml", async (c) => {
340
351
  }));
341
352
  response.headers.set("Content-Type", "application/atom+xml; charset=utf-8");
342
353
  return response;
343
- });
344
- app.post("/follow", async (c) => {
345
- const { bot } = c.env;
346
- const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);
354
+ }
355
+ app.post("/follow", (c) => followPage(c, c.env.bot, c.env.contextData, ""));
356
+ async function followPage(c, bot, contextData, base) {
357
+ const home = base === "" ? "/" : base;
358
+ const ctx = bot.federation.createContext(c.req.raw, contextData);
347
359
  const url = new URL(c.req.url);
348
360
  const formData = await c.req.formData();
349
361
  let followerHandle = formData.get("handle")?.toString();
@@ -358,7 +370,7 @@ app.post("/follow", async (c) => {
358
370
  /* @__PURE__ */ jsx("h1", { children: "Error" }),
359
371
  /* @__PURE__ */ jsx("p", { children: "Follower handle is required." }),
360
372
  /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("a", {
361
- href: "/",
373
+ href: home,
362
374
  children: "Go back"
363
375
  }) })
364
376
  ]
@@ -381,7 +393,7 @@ app.post("/follow", async (c) => {
381
393
  "."
382
394
  ] }),
383
395
  /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("a", {
384
- href: "/",
396
+ href: home,
385
397
  children: "Go back"
386
398
  }) })
387
399
  ]
@@ -409,7 +421,7 @@ app.post("/follow", async (c) => {
409
421
  "."
410
422
  ] }),
411
423
  /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("a", {
412
- href: "/",
424
+ href: home,
413
425
  children: "Go back"
414
426
  }) })
415
427
  ]
@@ -426,14 +438,73 @@ app.post("/follow", async (c) => {
426
438
  /* @__PURE__ */ jsx("h1", { children: "Internal Server Error" }),
427
439
  /* @__PURE__ */ jsx("p", { children: "An internal server error occurred while processing your request." }),
428
440
  /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("a", {
429
- href: "/",
441
+ href: home,
430
442
  children: "Go back"
431
443
  }) })
432
444
  ]
433
445
  })
434
446
  }), 500);
435
447
  }
448
+ }
449
+ const multiApp = new Hono();
450
+ multiApp.get("/", (c) => {
451
+ const { instance } = c.env;
452
+ const url = new URL(c.req.url);
453
+ const cssFilename = instance.pages.color === "azure" ? `pico.min.css` : `pico.${instance.pages.color}.min.css`;
454
+ const bots = [...instance.bots];
455
+ return c.html(/* @__PURE__ */ jsxs("html", { children: [/* @__PURE__ */ jsxs("head", { children: [
456
+ /* @__PURE__ */ jsx("meta", { charset: "utf-8" }),
457
+ /* @__PURE__ */ jsx("title", { children: url.host }),
458
+ /* @__PURE__ */ jsx("link", {
459
+ rel: "stylesheet",
460
+ href: `https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/${cssFilename}`
461
+ }),
462
+ /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: instance.pages.css } })
463
+ ] }), /* @__PURE__ */ jsxs("body", { children: [/* @__PURE__ */ jsx("header", {
464
+ class: "container",
465
+ children: /* @__PURE__ */ jsx("h1", { children: url.host })
466
+ }), /* @__PURE__ */ jsx("main", {
467
+ class: "container",
468
+ children: /* @__PURE__ */ jsx("ul", { children: bots.map((bot) => /* @__PURE__ */ jsxs("li", { children: [
469
+ /* @__PURE__ */ jsx("a", {
470
+ href: `/@${encodeURIComponent(bot.username)}`,
471
+ children: bot.name ?? bot.username
472
+ }),
473
+ " ",
474
+ /* @__PURE__ */ jsxs("span", {
475
+ style: "user-select: all;",
476
+ children: [
477
+ "@",
478
+ bot.username,
479
+ "@",
480
+ url.host
481
+ ]
482
+ })
483
+ ] })) })
484
+ })] })] }));
436
485
  });
486
+ /**
487
+ * Resolves the bot a multi-bot page belongs to, and delegates to the given
488
+ * page handler with the bot's base path.
489
+ */
490
+ function withBot(page) {
491
+ return async (c) => {
492
+ const { instance, contextData } = c.env;
493
+ const username = c.req.param("handle")?.slice(1);
494
+ if (username == null) return c.notFound();
495
+ const ctx = instance.federation.createContext(c.req.raw, contextData);
496
+ const bot = await instance.resolveBotByUsername(ctx, username);
497
+ if (bot == null) return c.notFound();
498
+ return await page(c, bot, contextData, `/@${encodeURIComponent(username)}`);
499
+ };
500
+ }
501
+ const handlePattern = ":handle{@[^/]+}";
502
+ multiApp.get(`/${handlePattern}`, withBot(profilePage));
503
+ multiApp.get(`/${handlePattern}/followers`, withBot(followersPage));
504
+ multiApp.get(`/${handlePattern}/tags/:hashtag`, withBot(hashtagPage));
505
+ multiApp.get(`/${handlePattern}/feed.xml`, withBot(feedPage));
506
+ multiApp.post(`/${handlePattern}/follow`, withBot(followPage));
507
+ multiApp.get(`/${handlePattern}/:id`, withBot(messagePage));
437
508
  async function getPosts(bot, ctx, options = {}) {
438
509
  const { offset, window = 15 } = options;
439
510
  let posts = await Array.fromAsync(bot.repository.getMessages({
@@ -491,5 +562,5 @@ function normalizeHashtag(hashtag) {
491
562
  }
492
563
 
493
564
  //#endregion
494
- export { app };
565
+ export { app, multiApp };
495
566
  //# sourceMappingURL=pages.js.map
package/dist/pages.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"pages.js","names":["properties: Record<string, string>","nextLink: URL | undefined","bot: BotImpl<unknown>","ctx: Context<unknown>","options: GetPostsOptions","lastPost: Announce | Create | undefined","nextPost: Object | undefined","post: Create | Announce","context: Context<unknown>","hashtag?: string","hashtag: string"],"sources":["../src/pages.tsx"],"sourcesContent":["// BotKit by Fedify: A framework for creating ActivityPub bots\n// Copyright (C) 2025–2026 Hong Minhee <https://hongminhee.org/>\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n/** @jsx react-jsx */\n/** @jsxImportSource hono/jsx */\nimport type { Context } from \"@fedify/fedify/federation\";\nimport {\n type Announce,\n type Create,\n getActorHandle,\n Hashtag,\n Image,\n Link,\n type Object,\n PUBLIC_COLLECTION,\n} from \"@fedify/vocab\";\nimport { Hono } from \"hono\";\nimport { decode } from \"html-entities\";\nimport { parseTemplate } from \"url-template\";\nimport type { BotImpl } from \"./bot-impl.ts\";\nimport { FollowButton } from \"./components/FollowButton.tsx\";\nimport { Follower } from \"./components/Follower.tsx\";\nimport { Layout } from \"./components/Layout.tsx\";\nimport { Message } from \"./components/Message.tsx\";\nimport { getMessageClass, isMessageObject, textXss } from \"./message-impl.ts\";\nimport type { MessageClass } from \"./message.ts\";\nimport type { Uuid } from \"./repository.ts\";\n\nexport interface Bindings {\n readonly bot: BotImpl<unknown>;\n readonly contextData: unknown;\n}\n\nexport interface Env {\n readonly Bindings: Bindings;\n}\n\nexport const app = new Hono<Env>();\n\napp.get(\"/\", async (c) => {\n const { bot } = c.env;\n const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);\n const session = bot.getSession(ctx);\n const url = new URL(c.req.url);\n const handle = `@${bot.username}@${url.host}`;\n const icon = bot.icon instanceof Image\n ? bot.icon.url instanceof Link ? bot.icon.url.href : bot.icon.url\n : bot.icon;\n const iconWidth = bot.icon instanceof Image ? bot.icon.width : null;\n const iconHeight = bot.icon instanceof Image ? bot.icon.height : null;\n const image = bot.image instanceof Image\n ? bot.image.url instanceof Link ? bot.image.url.href : bot.image.url\n : bot.image;\n const imageWidth = bot.image instanceof Image ? bot.image.width : null;\n const imageHeight = bot.image instanceof Image ? bot.image.height : null;\n const followersCount = await bot.repository.countFollowers();\n const summaryChunks = bot.summary?.getHtml(session);\n const postsCount = await bot.repository.countMessages();\n const summary = summaryChunks == null\n ? null\n : (await Array.fromAsync(summaryChunks)).join(\"\");\n const properties: Record<string, string> = {};\n for (const name in bot.properties) {\n const value = bot.properties[name];\n const valueHtml = (await Array.fromAsync(value.getHtml(session))).join(\"\");\n properties[name] = valueHtml;\n }\n const offset = c.req.query(\"offset\");\n const { posts: messages, nextPost } = await getPosts(\n bot,\n ctx,\n offset ? { offset: Temporal.Instant.from(offset) } : {},\n );\n const activityLink = ctx.getActorUri(bot.identifier);\n const feedLink = new URL(\"/feed.xml\", url);\n let nextLink: URL | undefined;\n if (nextPost?.published != null) {\n nextLink = new URL(\"/\", url);\n nextLink.searchParams.set(\"offset\", nextPost.published.toString());\n }\n return c.html(\n <Layout\n bot={bot}\n host={url.host}\n activityLink={activityLink}\n feedLink={feedLink}\n >\n <header class=\"container\">\n {image && (\n <img\n src={image.href}\n width={imageWidth ?? undefined}\n height={imageHeight ?? undefined}\n alt={image instanceof Image\n ? image.name?.toString() ?? undefined\n : undefined}\n style=\"width: 100%; margin-bottom: 1em;\"\n />\n )}\n <hgroup>\n {icon && (\n <img\n src={icon.href}\n width={iconWidth ?? undefined}\n height={iconHeight ?? undefined}\n style=\"float: left; margin-right: 1em; height: 72;\"\n />\n )}\n <h1>\n <a href=\"/\">{bot.name ?? bot.username}</a>\n </h1>\n <p>\n <span style=\"user-select: all;\">{handle}</span> &middot;{\" \"}\n <a\n href=\"/feed.xml\"\n rel=\"alternate\"\n type=\"application/atom+xml\"\n title=\"Atom feed\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={18}\n height={18}\n viewBox=\"0 0 16 16\"\n aria-label=\"Atom feed\"\n >\n <path\n fill=\"currentColor\"\n d=\"M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm1.5 2.5c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0a8 8 0 0 0-8-8a1 1 0 0 1 0-2m0 4a6 6 0 0 1 6 6a1 1 0 1 1-2 0a4 4 0 0 0-4-4a1 1 0 0 1 0-2m.5 7a1.5 1.5 0 1 1 0-3a1.5 1.5 0 0 1 0 3\"\n >\n </path>\n </svg>\n </a>{\" \"}\n &middot;{\" \"}\n <span>\n <a href=\"/followers\">\n {followersCount === 1\n ? `1 follower`\n : `${followersCount.toLocaleString(\"en\")} followers`}\n </a>\n </span>{\" \"}\n &middot;{\" \"}\n <span>\n {postsCount === 1\n ? `1 post`\n : `${postsCount.toLocaleString(\"en\")} posts`}\n </span>{\" \"}\n &middot; <FollowButton bot={bot} />\n </p>\n </hgroup>\n {summary &&\n (\n <div\n dangerouslySetInnerHTML={{ __html: summary }}\n />\n )}\n {globalThis.Object.keys(properties).length > 0 && (\n <table>\n <tbody>\n {globalThis.Object.entries(properties).map(([name, value]) => (\n <tr>\n <th scope=\"row\" style=\"width: 1%; white-space: nowrap;\">\n <strong>{name}</strong>\n </th>\n <td\n dangerouslySetInnerHTML={{ __html: value }}\n />\n </tr>\n ))}\n </tbody>\n </table>\n )}\n </header>\n <main class=\"container\">\n {messages.map((message) => (\n <Message message={message} session={session} />\n ))}\n </main>\n <footer class=\"container\">\n <nav style=\"display: block; text-align: end;\">\n {nextLink && (\n <a rel=\"next\" href={nextLink.href}>\n Older posts &rarr;\n </a>\n )}\n </nav>\n </footer>\n </Layout>,\n {\n headers: {\n Link:\n `<${activityLink.href}>; rel=\"alternate\"; type=\"application/activity+json\", ` +\n `<${feedLink.href}>; rel=\"alternate\"; type=\"application/atom+xml\"` +\n (nextLink\n ? `, <${nextLink.href}>; rel=\"next\"; type=\"text/html\"`\n : \"\"),\n },\n },\n );\n});\n\napp.get(\"/followers\", async (c) => {\n const { bot } = c.env;\n const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);\n const session = bot.getSession(ctx);\n const followersCount = await bot.repository.countFollowers();\n const followers = await Array.fromAsync(bot.repository.getFollowers());\n\n const url = new URL(c.req.url);\n const activityLink = ctx.getActorUri(bot.identifier);\n const feedLink = new URL(\"/feed.xml\", url);\n\n return c.html(\n <Layout\n bot={bot}\n host={url.host}\n activityLink={activityLink}\n feedLink={feedLink}\n >\n <header class=\"container\">\n <h1>\n <a href=\"/\">&larr;</a>{\" \"}\n {followersCount === 1\n ? `1 follower`\n : `${followersCount.toLocaleString(\"en\")} followers`}\n </h1>\n </header>\n <main class=\"container\">\n {followers.map((follower, index) => (\n <Follower\n key={follower.id?.href ?? index}\n actor={follower}\n session={session}\n />\n ))}\n </main>\n </Layout>,\n );\n});\n\napp.get(\"/tags/:hashtag\", async (c) => {\n const hashtag = c.req.param(\"hashtag\");\n const { bot } = c.env;\n const url = new URL(c.req.url);\n const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);\n const session = bot.getSession(ctx);\n const offset = c.req.query(\"offset\");\n const { posts, nextPost } = await getPosts(bot, ctx, {\n hashtag,\n offset: offset == null ? undefined : Temporal.Instant.from(offset),\n });\n let nextLink: URL | undefined;\n if (nextPost?.published != null) {\n nextLink = new URL(`/tags/${encodeURIComponent(hashtag)}`, url);\n nextLink.searchParams.set(\"offset\", nextPost.published.toString());\n }\n return c.html(\n <Layout bot={bot} host={url.host} title={`#${hashtag}`}>\n <header class=\"container\">\n <h1>#{hashtag}</h1>\n </header>\n <main class=\"container\">\n {posts.map((message) => (\n <Message message={message} session={session} />\n ))}\n </main>\n <footer class=\"container\">\n <nav style=\"display: block; text-align: end;\">\n {nextLink && (\n <a rel=\"next\" href={nextLink.href}>Older posts &rarr;</a>\n )}\n </nav>\n </footer>\n </Layout>,\n {\n headers: nextLink == null ? {} : {\n Link: `<${nextLink.href}>; rel=\"next\"; type=\"text/html\"`,\n },\n },\n );\n});\n\napp.get(\"/message/:id\", async (c) => {\n const id = c.req.param(\"id\");\n const { bot } = c.env;\n const url = new URL(c.req.url);\n const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);\n const session = bot.getSession(ctx);\n const post = await bot.repository.getMessage(id as Uuid);\n if (post == null || !isPublic(post)) return c.notFound();\n const message = await post.getObject(ctx);\n if (message == null || !isMessageObject(message)) return c.notFound();\n const activityLink = ctx.getObjectUri<MessageClass>(\n getMessageClass(message),\n { id },\n );\n const feedLink = new URL(\"/feed.xml\", url);\n let title = message.name;\n if (title == null) {\n title = message.summary ?? message.content;\n if (title != null) {\n title = decode(textXss.process(title.toString()));\n }\n }\n return c.html(\n <Layout\n bot={bot}\n host={url.host}\n activityLink={activityLink}\n feedLink={feedLink}\n title={title?.toString() ?? undefined}\n >\n <main class=\"container\">\n <Message message={message} session={session} />\n </main>\n </Layout>,\n {\n headers: {\n Link:\n `<${activityLink.href}>; rel=\"alternate\"; type=\"application/activity+json\", ` +\n `<${feedLink.href}>; rel=\"alternate\"; type=\"application/atom+xml\"`,\n },\n },\n );\n});\n\napp.get(\"/feed.xml\", async (c) => {\n const { bot } = c.env;\n const url = new URL(c.req.url);\n const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);\n const session = bot.getSession(ctx);\n const { posts } = await getPosts(bot, ctx, { window: 30 });\n const botName = bot.name ?? bot.username;\n const canonicalUrl = new URL(\"/feed.xml\", url);\n const profileUrl = new URL(\"/\", url);\n const actorUrl = ctx.getActorUri(bot.identifier);\n c.header(\n \"Link\",\n `<${actorUrl.href}>; rel=\"alternate\"; type=\"application/activity+json\", ` +\n `<${profileUrl.href}>; rel=\"alternate\"; type=\"text/html\"`,\n );\n const response = await c.render(\n <feed xmlns=\"http://www.w3.org/2005/Atom\">\n <id>{canonicalUrl.href}</id>\n <link rel=\"self\" type=\"application/atom+xml\" href={canonicalUrl.href} />\n <link rel=\"alternate\" type=\"text/html\" href={profileUrl.href} />\n <link\n rel=\"alternate\"\n type=\"application/activity+json\"\n href={actorUrl.href}\n />\n <title>{botName} (@{bot.username}@{url.host})</title>\n <author>\n <name>{botName}</name>\n <uri>{profileUrl.href}</uri>\n </author>\n {posts.length > 0 && (\n <updated>\n {(posts[0].updated ?? posts[0].published)?.toString()}\n </updated>\n )}\n {posts.map(async (post) => {\n const activityUrl = post.id;\n if (activityUrl == null) return undefined;\n const permalink =\n (post.url instanceof Link ? post.url.href : post.url) ?? activityUrl;\n const author = post.attributionId?.href === session.actorId?.href\n ? await session.getActor()\n : await post.getAttribution({\n documentLoader: ctx.documentLoader,\n contextLoader: ctx.contextLoader,\n suppressError: true,\n });\n const authorName = author?.name ?? author?.preferredUsername ??\n (author == null ? undefined : await getActorHandle(author));\n const authorUrl =\n (author?.url instanceof Link ? author.url.href : author?.url) ??\n author?.id;\n const updated = post.updated ?? post.published;\n let title = post.name;\n if (title == null) {\n title = post.summary ?? post.content;\n if (title != null) {\n title = decode(textXss.process(title.toString()));\n }\n }\n return (\n <entry>\n <id>{permalink.href}</id>\n <link rel=\"alternate\" type=\"text/html\" href={permalink.href} />\n <link\n rel=\"alternate\"\n type=\"application/activity+json\"\n href={activityUrl.href}\n />\n {authorName &&\n (\n <author>\n <name>{authorName}</name>\n {authorUrl &&\n <uri>{authorUrl.href}</uri>}\n </author>\n )}\n {post.published && (\n <published>{post.published.toString()}</published>\n )}\n {updated && <updated>{updated.toString()}</updated>}\n {title && <title>{title}</title>}\n {post.summary && (\n <summary type=\"html\">{post.summary.toString()}</summary>\n )}\n {post.content && (\n <content type=\"html\">{post.content.toString()}</content>\n )}\n </entry>\n );\n })}\n </feed>,\n );\n response.headers.set(\"Content-Type\", \"application/atom+xml; charset=utf-8\");\n return response;\n});\n\napp.post(\"/follow\", async (c) => {\n const { bot } = c.env;\n const ctx = bot.federation.createContext(c.req.raw, c.env.contextData);\n const url = new URL(c.req.url);\n\n const formData = await c.req.formData();\n let followerHandle = formData.get(\"handle\")?.toString();\n\n try {\n if (!followerHandle) {\n return c.html(\n <Layout bot={bot} host={url.host} title=\"Error\">\n <main class=\"container\">\n <h1>Error</h1>\n <p>Follower handle is required.</p>\n <p>\n <a href=\"/\">Go back</a>\n </p>\n </main>\n </Layout>,\n 400,\n );\n }\n\n if (followerHandle.startsWith(\"@\")) {\n followerHandle = followerHandle.slice(1);\n }\n\n const webfingerData = await ctx\n .lookupWebFinger(`acct:${followerHandle}`);\n\n if (!webfingerData?.links) {\n return c.html(\n <Layout bot={bot} host={url.host} title=\"Error\">\n <main class=\"container\">\n <h1>Error</h1>\n <p>\n No links found in webfinger data for{\" \"}\n <code>@{followerHandle}</code>.\n </p>\n <p>\n <a href=\"/\">Go back</a>\n </p>\n </main>\n </Layout>,\n 400,\n );\n }\n\n const subscribeLink = webfingerData.links.find(\n (link) => link.rel === \"http://ostatus.org/schema/1.0/subscribe\",\n );\n\n if (subscribeLink?.template) {\n const botActorUri = ctx.getActorUri(bot.identifier);\n const followUrlTemplate = parseTemplate(subscribeLink.template);\n const followUrl = followUrlTemplate.expand({\n uri: botActorUri.href,\n });\n return c.redirect(followUrl);\n }\n\n return c.html(\n <Layout bot={bot} host={url.host} title=\"Error\">\n <main class=\"container\">\n <h1>Error</h1>\n <p>\n No follow link found in WebFinger data for{\" \"}\n <code>@{followerHandle}</code>.\n </p>\n <p>\n <a href=\"/\">Go back</a>\n </p>\n </main>\n </Layout>,\n 400,\n );\n } catch (_error) {\n return c.html(\n <Layout bot={bot} host={url.host} title=\"Error\">\n <main class=\"container\">\n <h1>Internal Server Error</h1>\n <p>\n An internal server error occurred while processing your request.\n </p>\n <p>\n <a href=\"/\">Go back</a>\n </p>\n </main>\n </Layout>,\n 500,\n );\n }\n});\n\ninterface GetPostsOptions {\n readonly hashtag?: string;\n readonly offset?: Temporal.Instant;\n readonly window?: number;\n}\n\nasync function getPosts(\n bot: BotImpl<unknown>,\n ctx: Context<unknown>,\n options: GetPostsOptions = {},\n): Promise<{ posts: MessageClass[]; nextPost?: Object }> {\n const { offset, window = 15 } = options;\n let posts = await Array.fromAsync(\n bot.repository.getMessages({\n order: \"newest\",\n until: offset,\n limit: window * 2,\n }),\n );\n let lastPost: Announce | Create | undefined = posts[posts.length - 1];\n posts = posts.slice(0, posts.length - 1);\n posts = posts.filter(isPublic);\n if (options.hashtag != null) {\n const taggedPosts = [];\n for (const post of posts) {\n if (await hasHashtag(ctx, post, options.hashtag)) {\n taggedPosts.push(post);\n }\n }\n posts = taggedPosts;\n }\n while (lastPost != null && posts.length < window) {\n const limit = (window - posts.length) * 2;\n const until = lastPost.published ??\n (await lastPost.getObject(ctx))?.published ??\n undefined;\n if (until == null) break;\n const nextPosts = bot.repository.getMessages({\n order: \"newest\",\n until,\n limit,\n });\n let i = 0;\n lastPost = undefined;\n for await (const post of nextPosts) {\n if (\n isPublic(post) && await hasHashtag(ctx, post, options.hashtag) &&\n posts.length < window + 1\n ) posts.push(post);\n lastPost = post;\n i++;\n }\n if (i < limit) break;\n }\n const nextPost: Object | undefined = await posts[window]?.getObject(ctx) ??\n undefined;\n posts = posts.slice(0, window);\n const messages = (await Promise.all(posts.map((p) => p.getObject(ctx))))\n .filter(isMessageObject);\n return { posts: messages, nextPost };\n}\n\nfunction isPublic(post: Create | Announce): boolean {\n return post.toIds.some((url) => url.href === PUBLIC_COLLECTION.href) ||\n post.ccIds.some((url) => url.href === PUBLIC_COLLECTION.href);\n}\n\nasync function hasHashtag(\n context: Context<unknown>,\n post: Create | Announce,\n hashtag?: string,\n): Promise<boolean> {\n if (hashtag == null) return true;\n hashtag = normalizeHashtag(hashtag);\n const object = await post.getObject(context);\n if (object == null) return false;\n for await (const tag of object.getTags(context)) {\n if (\n tag instanceof Hashtag && tag.name != null &&\n normalizeHashtag(tag.name.toString()) === hashtag\n ) {\n return true;\n }\n }\n return false;\n}\n\nfunction normalizeHashtag(hashtag: string): string {\n return hashtag\n .toLowerCase()\n .trimStart()\n .replace(/^#/, \"\")\n .trim()\n .replace(/\\s+/g, \"\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pages.js","names":["c: PageContext","bot: BotImpl<unknown>","contextData: unknown","base: string","properties: Record<string, string>","nextLink: URL | undefined","page: (\n c: PageContext,\n bot: BotImpl<unknown>,\n contextData: unknown,\n base: string,\n ) => Promise<Response>","ctx: Context<unknown>","options: GetPostsOptions","lastPost: Announce | Create | undefined","nextPost: Object | undefined","post: Create | Announce","context: Context<unknown>","hashtag?: string","hashtag: string"],"sources":["../src/pages.tsx"],"sourcesContent":["// BotKit by Fedify: A framework for creating ActivityPub bots\n// Copyright (C) 2025–2026 Hong Minhee <https://hongminhee.org/>\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n/** @jsx react-jsx */\n/** @jsxImportSource hono/jsx */\nimport type { Context } from \"@fedify/fedify/federation\";\nimport {\n type Announce,\n type Create,\n getActorHandle,\n Hashtag,\n Image,\n Link,\n type Object,\n PUBLIC_COLLECTION,\n} from \"@fedify/vocab\";\nimport { Hono } from \"hono\";\nimport { decode } from \"html-entities\";\nimport { parseTemplate } from \"url-template\";\nimport type { Context as HonoContext } from \"hono\";\nimport type { BotImpl } from \"./bot-impl.ts\";\nimport { FollowButton } from \"./components/FollowButton.tsx\";\nimport { Follower } from \"./components/Follower.tsx\";\nimport { Layout } from \"./components/Layout.tsx\";\nimport { Message } from \"./components/Message.tsx\";\nimport type { InstanceImpl } from \"./instance-impl.ts\";\nimport { getMessageClass, isMessageObject, textXss } from \"./message-impl.ts\";\nimport type { MessageClass } from \"./message.ts\";\nimport type { Uuid } from \"./repository.ts\";\n\nexport interface Bindings {\n readonly bot: BotImpl<unknown>;\n readonly contextData: unknown;\n}\n\nexport interface Env {\n readonly Bindings: Bindings;\n}\n\nexport interface InstanceBindings {\n readonly instance: InstanceImpl<unknown>;\n readonly contextData: unknown;\n}\n\nexport interface InstanceEnv {\n readonly Bindings: InstanceBindings;\n}\n\n// deno-lint-ignore no-explicit-any\ntype PageContext = HonoContext<any>;\n\nexport const app = new Hono<Env>();\n\napp.get(\"/\", (c) => profilePage(c, c.env.bot, c.env.contextData, \"\"));\n\nasync function profilePage(\n c: PageContext,\n bot: BotImpl<unknown>,\n contextData: unknown,\n base: string,\n): Promise<Response> {\n const home = base === \"\" ? \"/\" : base;\n const ctx = bot.federation.createContext(c.req.raw, contextData);\n const session = bot.getSession(ctx);\n const url = new URL(c.req.url);\n const handle = `@${bot.username}@${url.host}`;\n const icon = bot.icon instanceof Image\n ? bot.icon.url instanceof Link ? bot.icon.url.href : bot.icon.url\n : bot.icon;\n const iconWidth = bot.icon instanceof Image ? bot.icon.width : null;\n const iconHeight = bot.icon instanceof Image ? bot.icon.height : null;\n const image = bot.image instanceof Image\n ? bot.image.url instanceof Link ? bot.image.url.href : bot.image.url\n : bot.image;\n const imageWidth = bot.image instanceof Image ? bot.image.width : null;\n const imageHeight = bot.image instanceof Image ? bot.image.height : null;\n const followersCount = await bot.repository.countFollowers();\n const summaryChunks = bot.summary?.getHtml(session);\n const postsCount = await bot.repository.countMessages();\n const summary = summaryChunks == null\n ? null\n : (await Array.fromAsync(summaryChunks)).join(\"\");\n const properties: Record<string, string> = {};\n for (const name in bot.properties) {\n const value = bot.properties[name];\n const valueHtml = (await Array.fromAsync(value.getHtml(session))).join(\"\");\n properties[name] = valueHtml;\n }\n const offset = c.req.query(\"offset\");\n const { posts: messages, nextPost } = await getPosts(\n bot,\n ctx,\n offset ? { offset: Temporal.Instant.from(offset) } : {},\n );\n const activityLink = ctx.getActorUri(bot.identifier);\n const feedLink = new URL(`${base}/feed.xml`, url);\n let nextLink: URL | undefined;\n if (nextPost?.published != null) {\n nextLink = new URL(home, url);\n nextLink.searchParams.set(\"offset\", nextPost.published.toString());\n }\n return c.html(\n <Layout\n bot={bot}\n host={url.host}\n activityLink={activityLink}\n feedLink={feedLink}\n >\n <header class=\"container\">\n {image && (\n <img\n src={image.href}\n width={imageWidth ?? undefined}\n height={imageHeight ?? undefined}\n alt={image instanceof Image\n ? image.name?.toString() ?? undefined\n : undefined}\n style=\"width: 100%; margin-bottom: 1em;\"\n />\n )}\n <hgroup>\n {icon && (\n <img\n src={icon.href}\n width={iconWidth ?? undefined}\n height={iconHeight ?? undefined}\n style=\"float: left; margin-right: 1em; height: 72;\"\n />\n )}\n <h1>\n <a href={home}>{bot.name ?? bot.username}</a>\n </h1>\n <p>\n <span style=\"user-select: all;\">{handle}</span> &middot;{\" \"}\n <a\n href={`${base}/feed.xml`}\n rel=\"alternate\"\n type=\"application/atom+xml\"\n title=\"Atom feed\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={18}\n height={18}\n viewBox=\"0 0 16 16\"\n aria-label=\"Atom feed\"\n >\n <path\n fill=\"currentColor\"\n d=\"M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm1.5 2.5c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0a8 8 0 0 0-8-8a1 1 0 0 1 0-2m0 4a6 6 0 0 1 6 6a1 1 0 1 1-2 0a4 4 0 0 0-4-4a1 1 0 0 1 0-2m.5 7a1.5 1.5 0 1 1 0-3a1.5 1.5 0 0 1 0 3\"\n >\n </path>\n </svg>\n </a>{\" \"}\n &middot;{\" \"}\n <span>\n <a href={`${base}/followers`}>\n {followersCount === 1\n ? `1 follower`\n : `${followersCount.toLocaleString(\"en\")} followers`}\n </a>\n </span>{\" \"}\n &middot;{\" \"}\n <span>\n {postsCount === 1\n ? `1 post`\n : `${postsCount.toLocaleString(\"en\")} posts`}\n </span>{\" \"}\n &middot; <FollowButton bot={bot} action={`${base}/follow`} />\n </p>\n </hgroup>\n {summary &&\n (\n <div\n dangerouslySetInnerHTML={{ __html: summary }}\n />\n )}\n {globalThis.Object.keys(properties).length > 0 && (\n <table>\n <tbody>\n {globalThis.Object.entries(properties).map(([name, value]) => (\n <tr>\n <th scope=\"row\" style=\"width: 1%; white-space: nowrap;\">\n <strong>{name}</strong>\n </th>\n <td\n dangerouslySetInnerHTML={{ __html: value }}\n />\n </tr>\n ))}\n </tbody>\n </table>\n )}\n </header>\n <main class=\"container\">\n {messages.map((message) => (\n <Message message={message} session={session} />\n ))}\n </main>\n <footer class=\"container\">\n <nav style=\"display: block; text-align: end;\">\n {nextLink && (\n <a rel=\"next\" href={nextLink.href}>\n Older posts &rarr;\n </a>\n )}\n </nav>\n </footer>\n </Layout>,\n {\n headers: {\n Link:\n `<${activityLink.href}>; rel=\"alternate\"; type=\"application/activity+json\", ` +\n `<${feedLink.href}>; rel=\"alternate\"; type=\"application/atom+xml\"` +\n (nextLink\n ? `, <${nextLink.href}>; rel=\"next\"; type=\"text/html\"`\n : \"\"),\n },\n },\n );\n}\n\napp.get(\n \"/followers\",\n (c) => followersPage(c, c.env.bot, c.env.contextData, \"\"),\n);\n\nasync function followersPage(\n c: PageContext,\n bot: BotImpl<unknown>,\n contextData: unknown,\n base: string,\n): Promise<Response> {\n const home = base === \"\" ? \"/\" : base;\n const ctx = bot.federation.createContext(c.req.raw, contextData);\n const session = bot.getSession(ctx);\n const followersCount = await bot.repository.countFollowers();\n const followers = await Array.fromAsync(bot.repository.getFollowers());\n\n const url = new URL(c.req.url);\n const activityLink = ctx.getActorUri(bot.identifier);\n const feedLink = new URL(`${base}/feed.xml`, url);\n\n return c.html(\n <Layout\n bot={bot}\n host={url.host}\n activityLink={activityLink}\n feedLink={feedLink}\n >\n <header class=\"container\">\n <h1>\n <a href={home}>&larr;</a>{\" \"}\n {followersCount === 1\n ? `1 follower`\n : `${followersCount.toLocaleString(\"en\")} followers`}\n </h1>\n </header>\n <main class=\"container\">\n {followers.map((follower, index) => (\n <Follower\n key={follower.id?.href ?? index}\n actor={follower}\n session={session}\n />\n ))}\n </main>\n </Layout>,\n );\n}\n\napp.get(\n \"/tags/:hashtag\",\n (c) => hashtagPage(c, c.env.bot, c.env.contextData, \"\"),\n);\n\nasync function hashtagPage(\n c: PageContext,\n bot: BotImpl<unknown>,\n contextData: unknown,\n base: string,\n): Promise<Response> {\n const hashtag = c.req.param(\"hashtag\");\n if (hashtag == null) return c.notFound();\n const url = new URL(c.req.url);\n const ctx = bot.federation.createContext(c.req.raw, contextData);\n const session = bot.getSession(ctx);\n const offset = c.req.query(\"offset\");\n const { posts, nextPost } = await getPosts(bot, ctx, {\n hashtag,\n offset: offset == null ? undefined : Temporal.Instant.from(offset),\n });\n let nextLink: URL | undefined;\n if (nextPost?.published != null) {\n nextLink = new URL(`${base}/tags/${encodeURIComponent(hashtag)}`, url);\n nextLink.searchParams.set(\"offset\", nextPost.published.toString());\n }\n return c.html(\n <Layout bot={bot} host={url.host} title={`#${hashtag}`}>\n <header class=\"container\">\n <h1>#{hashtag}</h1>\n </header>\n <main class=\"container\">\n {posts.map((message) => (\n <Message\n message={message}\n session={session}\n />\n ))}\n </main>\n <footer class=\"container\">\n <nav style=\"display: block; text-align: end;\">\n {nextLink && (\n <a rel=\"next\" href={nextLink.href}>\n Older posts &rarr;\n </a>\n )}\n </nav>\n </footer>\n </Layout>,\n {\n headers: nextLink == null ? {} : {\n Link: `<${nextLink.href}>; rel=\"next\"; type=\"text/html\"`,\n },\n },\n );\n}\n\napp.get(\n \"/message/:id\",\n (c) => messagePage(c, c.env.bot, c.env.contextData, \"\"),\n);\n\nasync function messagePage(\n c: PageContext,\n bot: BotImpl<unknown>,\n contextData: unknown,\n base: string,\n): Promise<Response> {\n const id = c.req.param(\"id\");\n if (id == null) return c.notFound();\n const url = new URL(c.req.url);\n const ctx = bot.federation.createContext(c.req.raw, contextData);\n const session = bot.getSession(ctx);\n const post = await bot.repository.getMessage(id as Uuid);\n if (post == null || !isPublic(post)) return c.notFound();\n const message = await post.getObject(ctx);\n if (message == null || !isMessageObject(message)) return c.notFound();\n const activityLink = ctx.getObjectUri<MessageClass>(\n getMessageClass(message),\n { identifier: bot.identifier, id },\n );\n const feedLink = new URL(`${base}/feed.xml`, url);\n let title = message.name;\n if (title == null) {\n title = message.summary ?? message.content;\n if (title != null) {\n title = decode(textXss.process(title.toString()));\n }\n }\n return c.html(\n <Layout\n bot={bot}\n host={url.host}\n activityLink={activityLink}\n feedLink={feedLink}\n title={title?.toString() ?? undefined}\n >\n <main class=\"container\">\n <Message message={message} session={session} />\n </main>\n </Layout>,\n {\n headers: {\n Link:\n `<${activityLink.href}>; rel=\"alternate\"; type=\"application/activity+json\", ` +\n `<${feedLink.href}>; rel=\"alternate\"; type=\"application/atom+xml\"`,\n },\n },\n );\n}\n\napp.get(\"/feed.xml\", (c) => feedPage(c, c.env.bot, c.env.contextData, \"\"));\n\nasync function feedPage(\n c: PageContext,\n bot: BotImpl<unknown>,\n contextData: unknown,\n base: string,\n): Promise<Response> {\n const home = base === \"\" ? \"/\" : base;\n const url = new URL(c.req.url);\n const ctx = bot.federation.createContext(c.req.raw, contextData);\n const session = bot.getSession(ctx);\n const { posts } = await getPosts(bot, ctx, { window: 30 });\n const botName = bot.name ?? bot.username;\n const canonicalUrl = new URL(`${base}/feed.xml`, url);\n const profileUrl = new URL(home, url);\n const actorUrl = ctx.getActorUri(bot.identifier);\n c.header(\n \"Link\",\n `<${actorUrl.href}>; rel=\"alternate\"; type=\"application/activity+json\", ` +\n `<${profileUrl.href}>; rel=\"alternate\"; type=\"text/html\"`,\n );\n const response = await c.render(\n <feed xmlns=\"http://www.w3.org/2005/Atom\">\n <id>{canonicalUrl.href}</id>\n <link rel=\"self\" type=\"application/atom+xml\" href={canonicalUrl.href} />\n <link rel=\"alternate\" type=\"text/html\" href={profileUrl.href} />\n <link\n rel=\"alternate\"\n type=\"application/activity+json\"\n href={actorUrl.href}\n />\n <title>{botName} (@{bot.username}@{url.host})</title>\n <author>\n <name>{botName}</name>\n <uri>{profileUrl.href}</uri>\n </author>\n {posts.length > 0 && (\n <updated>\n {(posts[0].updated ?? posts[0].published)?.toString()}\n </updated>\n )}\n {posts.map(async (post) => {\n const activityUrl = post.id;\n if (activityUrl == null) return undefined;\n const permalink =\n (post.url instanceof Link ? post.url.href : post.url) ?? activityUrl;\n const author = post.attributionId?.href === session.actorId?.href\n ? await session.getActor()\n : await post.getAttribution({\n documentLoader: ctx.documentLoader,\n contextLoader: ctx.contextLoader,\n suppressError: true,\n });\n const authorName = author?.name ?? author?.preferredUsername ??\n (author == null ? undefined : await getActorHandle(author));\n const authorUrl =\n (author?.url instanceof Link ? author.url.href : author?.url) ??\n author?.id;\n const updated = post.updated ?? post.published;\n let title = post.name;\n if (title == null) {\n title = post.summary ?? post.content;\n if (title != null) {\n title = decode(textXss.process(title.toString()));\n }\n }\n return (\n <entry>\n <id>{permalink.href}</id>\n <link rel=\"alternate\" type=\"text/html\" href={permalink.href} />\n <link\n rel=\"alternate\"\n type=\"application/activity+json\"\n href={activityUrl.href}\n />\n {authorName &&\n (\n <author>\n <name>{authorName}</name>\n {authorUrl &&\n <uri>{authorUrl.href}</uri>}\n </author>\n )}\n {post.published && (\n <published>{post.published.toString()}</published>\n )}\n {updated && <updated>{updated.toString()}</updated>}\n {title && <title>{title}</title>}\n {post.summary && (\n <summary type=\"html\">{post.summary.toString()}</summary>\n )}\n {post.content && (\n <content type=\"html\">{post.content.toString()}</content>\n )}\n </entry>\n );\n })}\n </feed>,\n );\n response.headers.set(\"Content-Type\", \"application/atom+xml; charset=utf-8\");\n return response;\n}\n\napp.post(\"/follow\", (c) => followPage(c, c.env.bot, c.env.contextData, \"\"));\n\nasync function followPage(\n c: PageContext,\n bot: BotImpl<unknown>,\n contextData: unknown,\n base: string,\n): Promise<Response> {\n const home = base === \"\" ? \"/\" : base;\n const ctx = bot.federation.createContext(c.req.raw, contextData);\n const url = new URL(c.req.url);\n\n const formData = await c.req.formData();\n let followerHandle = formData.get(\"handle\")?.toString();\n\n try {\n if (!followerHandle) {\n return c.html(\n <Layout bot={bot} host={url.host} title=\"Error\">\n <main class=\"container\">\n <h1>Error</h1>\n <p>Follower handle is required.</p>\n <p>\n <a href={home}>Go back</a>\n </p>\n </main>\n </Layout>,\n 400,\n );\n }\n\n if (followerHandle.startsWith(\"@\")) {\n followerHandle = followerHandle.slice(1);\n }\n\n const webfingerData = await ctx\n .lookupWebFinger(`acct:${followerHandle}`);\n\n if (!webfingerData?.links) {\n return c.html(\n <Layout bot={bot} host={url.host} title=\"Error\">\n <main class=\"container\">\n <h1>Error</h1>\n <p>\n No links found in webfinger data for{\" \"}\n <code>@{followerHandle}</code>.\n </p>\n <p>\n <a href={home}>Go back</a>\n </p>\n </main>\n </Layout>,\n 400,\n );\n }\n\n const subscribeLink = webfingerData.links.find(\n (link) => link.rel === \"http://ostatus.org/schema/1.0/subscribe\",\n );\n\n if (subscribeLink?.template) {\n const botActorUri = ctx.getActorUri(bot.identifier);\n const followUrlTemplate = parseTemplate(subscribeLink.template);\n const followUrl = followUrlTemplate.expand({\n uri: botActorUri.href,\n });\n return c.redirect(followUrl);\n }\n\n return c.html(\n <Layout bot={bot} host={url.host} title=\"Error\">\n <main class=\"container\">\n <h1>Error</h1>\n <p>\n No follow link found in WebFinger data for{\" \"}\n <code>@{followerHandle}</code>.\n </p>\n <p>\n <a href={home}>Go back</a>\n </p>\n </main>\n </Layout>,\n 400,\n );\n } catch (_error) {\n return c.html(\n <Layout bot={bot} host={url.host} title=\"Error\">\n <main class=\"container\">\n <h1>Internal Server Error</h1>\n <p>\n An internal server error occurred while processing your request.\n </p>\n <p>\n <a href={home}>Go back</a>\n </p>\n </main>\n </Layout>,\n 500,\n );\n }\n}\n\nexport const multiApp = new Hono<InstanceEnv>();\n\nmultiApp.get(\"/\", (c) => {\n const { instance } = c.env;\n const url = new URL(c.req.url);\n const cssFilename = instance.pages.color === \"azure\"\n ? `pico.min.css`\n : `pico.${instance.pages.color}.min.css`;\n const bots = [...instance.bots];\n return c.html(\n <html>\n <head>\n <meta charset=\"utf-8\" />\n <title>{url.host}</title>\n <link\n rel=\"stylesheet\"\n href={`https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/${cssFilename}`}\n />\n <style dangerouslySetInnerHTML={{ __html: instance.pages.css }} />\n </head>\n <body>\n <header class=\"container\">\n <h1>{url.host}</h1>\n </header>\n <main class=\"container\">\n <ul>\n {bots.map((bot) => (\n <li>\n <a href={`/@${encodeURIComponent(bot.username)}`}>\n {bot.name ?? bot.username}\n </a>{\" \"}\n <span style=\"user-select: all;\">\n @{bot.username}@{url.host}\n </span>\n </li>\n ))}\n </ul>\n </main>\n </body>\n </html>,\n );\n});\n\n/**\n * Resolves the bot a multi-bot page belongs to, and delegates to the given\n * page handler with the bot's base path.\n */\nfunction withBot(\n page: (\n c: PageContext,\n bot: BotImpl<unknown>,\n contextData: unknown,\n base: string,\n ) => Promise<Response>,\n): (c: HonoContext<InstanceEnv>) => Promise<Response> {\n return async (c) => {\n const { instance, contextData } = c.env;\n const username = c.req.param(\"handle\")?.slice(1);\n if (username == null) return c.notFound();\n const ctx = instance.federation.createContext(c.req.raw, contextData);\n const bot = await instance.resolveBotByUsername(ctx, username);\n if (bot == null) return c.notFound();\n return await page(\n c,\n bot,\n contextData,\n `/@${encodeURIComponent(username)}`,\n );\n };\n}\n\nconst handlePattern = \":handle{@[^/]+}\";\n\nmultiApp.get(`/${handlePattern}`, withBot(profilePage));\nmultiApp.get(`/${handlePattern}/followers`, withBot(followersPage));\nmultiApp.get(`/${handlePattern}/tags/:hashtag`, withBot(hashtagPage));\nmultiApp.get(`/${handlePattern}/feed.xml`, withBot(feedPage));\nmultiApp.post(`/${handlePattern}/follow`, withBot(followPage));\nmultiApp.get(`/${handlePattern}/:id`, withBot(messagePage));\n\ninterface GetPostsOptions {\n readonly hashtag?: string;\n readonly offset?: Temporal.Instant;\n readonly window?: number;\n}\n\nasync function getPosts(\n bot: BotImpl<unknown>,\n ctx: Context<unknown>,\n options: GetPostsOptions = {},\n): Promise<{ posts: MessageClass[]; nextPost?: Object }> {\n const { offset, window = 15 } = options;\n let posts = await Array.fromAsync(\n bot.repository.getMessages({\n order: \"newest\",\n until: offset,\n limit: window * 2,\n }),\n );\n let lastPost: Announce | Create | undefined = posts[posts.length - 1];\n posts = posts.slice(0, posts.length - 1);\n posts = posts.filter(isPublic);\n if (options.hashtag != null) {\n const taggedPosts = [];\n for (const post of posts) {\n if (await hasHashtag(ctx, post, options.hashtag)) {\n taggedPosts.push(post);\n }\n }\n posts = taggedPosts;\n }\n while (lastPost != null && posts.length < window) {\n const limit = (window - posts.length) * 2;\n const until = lastPost.published ??\n (await lastPost.getObject(ctx))?.published ??\n undefined;\n if (until == null) break;\n const nextPosts = bot.repository.getMessages({\n order: \"newest\",\n until,\n limit,\n });\n let i = 0;\n lastPost = undefined;\n for await (const post of nextPosts) {\n if (\n isPublic(post) && await hasHashtag(ctx, post, options.hashtag) &&\n posts.length < window + 1\n ) posts.push(post);\n lastPost = post;\n i++;\n }\n if (i < limit) break;\n }\n const nextPost: Object | undefined = await posts[window]?.getObject(ctx) ??\n undefined;\n posts = posts.slice(0, window);\n const messages = (await Promise.all(posts.map((p) => p.getObject(ctx))))\n .filter(isMessageObject);\n return { posts: messages, nextPost };\n}\n\nfunction isPublic(post: Create | Announce): boolean {\n return post.toIds.some((url) => url.href === PUBLIC_COLLECTION.href) ||\n post.ccIds.some((url) => url.href === PUBLIC_COLLECTION.href);\n}\n\nasync function hasHashtag(\n context: Context<unknown>,\n post: Create | Announce,\n hashtag?: string,\n): Promise<boolean> {\n if (hashtag == null) return true;\n hashtag = normalizeHashtag(hashtag);\n const object = await post.getObject(context);\n if (object == null) return false;\n for await (const tag of object.getTags(context)) {\n if (\n tag instanceof Hashtag && tag.name != null &&\n normalizeHashtag(tag.name.toString()) === hashtag\n ) {\n return true;\n }\n }\n return false;\n}\n\nfunction normalizeHashtag(hashtag: string): string {\n return hashtag\n .toLowerCase()\n .trimStart()\n .replace(/^#/, \"\")\n .trim()\n .replace(/\\s+/g, \"\");\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Temporal, toTemporalInstant } from "@js-temporal/polyfill";
2
+ Date.prototype.toTemporalInstant = toTemporalInstant;