@geekity/cms 0.8.0 → 0.10.0

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 (236) hide show
  1. package/README.md +344 -47
  2. package/admin/components/admin-bar.njk +26 -0
  3. package/admin/components/public-admin-bar.njk +41 -0
  4. package/admin/layouts/base.njk +1 -0
  5. package/admin/layouts/shell.njk +8 -19
  6. package/admin/pages/comments/all.njk +3 -0
  7. package/admin/pages/settings/discussion.njk +53 -0
  8. package/admin/pages/settings/email.njk +20 -0
  9. package/admin/pages/tools/personal-data.njk +67 -0
  10. package/admin/pages/users/edit.njk +1 -1
  11. package/admin/static/admin-bar.css +137 -0
  12. package/admin/static/admin.css +1 -102
  13. package/dist/admin/comments.d.ts +5 -0
  14. package/dist/admin/comments.d.ts.map +1 -1
  15. package/dist/admin/comments.js +15 -1
  16. package/dist/admin/comments.js.map +1 -1
  17. package/dist/admin/headers.d.ts +14 -5
  18. package/dist/admin/headers.d.ts.map +1 -1
  19. package/dist/admin/headers.js +18 -6
  20. package/dist/admin/headers.js.map +1 -1
  21. package/dist/admin/index.d.ts +2 -2
  22. package/dist/admin/index.d.ts.map +1 -1
  23. package/dist/admin/index.js +1 -1
  24. package/dist/admin/index.js.map +1 -1
  25. package/dist/admin/menu.d.ts.map +1 -1
  26. package/dist/admin/menu.js +5 -1
  27. package/dist/admin/menu.js.map +1 -1
  28. package/dist/admin/personal-data.d.ts +35 -0
  29. package/dist/admin/personal-data.d.ts.map +1 -0
  30. package/dist/admin/personal-data.js +94 -0
  31. package/dist/admin/personal-data.js.map +1 -0
  32. package/dist/admin/preview.d.ts.map +1 -1
  33. package/dist/admin/preview.js +1 -1
  34. package/dist/admin/preview.js.map +1 -1
  35. package/dist/admin/routes.d.ts.map +1 -1
  36. package/dist/admin/routes.js +20 -3
  37. package/dist/admin/routes.js.map +1 -1
  38. package/dist/admin/session.d.ts +42 -1
  39. package/dist/admin/session.d.ts.map +1 -1
  40. package/dist/admin/session.js +74 -4
  41. package/dist/admin/session.js.map +1 -1
  42. package/dist/admin/settings-discussion.d.ts.map +1 -1
  43. package/dist/admin/settings-discussion.js +9 -1
  44. package/dist/admin/settings-discussion.js.map +1 -1
  45. package/dist/admin/settings-email.d.ts.map +1 -1
  46. package/dist/admin/settings-email.js +10 -1
  47. package/dist/admin/settings-email.js.map +1 -1
  48. package/dist/admin/settings.d.ts +39 -0
  49. package/dist/admin/settings.d.ts.map +1 -1
  50. package/dist/admin/settings.js +144 -0
  51. package/dist/admin/settings.js.map +1 -1
  52. package/dist/admin/store.d.ts +65 -0
  53. package/dist/admin/store.d.ts.map +1 -1
  54. package/dist/admin/store.js +130 -5
  55. package/dist/admin/store.js.map +1 -1
  56. package/dist/admin/templates.d.ts +2 -0
  57. package/dist/admin/templates.d.ts.map +1 -1
  58. package/dist/admin/templates.js +2 -0
  59. package/dist/admin/templates.js.map +1 -1
  60. package/dist/admin/tools.d.ts +2 -0
  61. package/dist/admin/tools.d.ts.map +1 -1
  62. package/dist/admin/tools.js +3 -2
  63. package/dist/admin/tools.js.map +1 -1
  64. package/dist/avatars/avatars.d.ts +112 -0
  65. package/dist/avatars/avatars.d.ts.map +1 -0
  66. package/dist/avatars/avatars.js +259 -0
  67. package/dist/avatars/avatars.js.map +1 -0
  68. package/dist/avatars/index.d.ts +4 -0
  69. package/dist/avatars/index.d.ts.map +1 -0
  70. package/dist/avatars/index.js +3 -0
  71. package/dist/avatars/index.js.map +1 -0
  72. package/dist/avatars/routes.d.ts +11 -0
  73. package/dist/avatars/routes.d.ts.map +1 -0
  74. package/dist/avatars/routes.js +33 -0
  75. package/dist/avatars/routes.js.map +1 -0
  76. package/dist/comments/index.d.ts +2 -2
  77. package/dist/comments/index.d.ts.map +1 -1
  78. package/dist/comments/index.js +1 -1
  79. package/dist/comments/index.js.map +1 -1
  80. package/dist/comments/records.d.ts +58 -2
  81. package/dist/comments/records.d.ts.map +1 -1
  82. package/dist/comments/records.js +244 -47
  83. package/dist/comments/records.js.map +1 -1
  84. package/dist/comments/routes.d.ts.map +1 -1
  85. package/dist/comments/routes.js +8 -2
  86. package/dist/comments/routes.js.map +1 -1
  87. package/dist/comments/viewer.d.ts +13 -0
  88. package/dist/comments/viewer.d.ts.map +1 -1
  89. package/dist/comments/viewer.js +13 -1
  90. package/dist/comments/viewer.js.map +1 -1
  91. package/dist/config.d.ts +35 -0
  92. package/dist/config.d.ts.map +1 -1
  93. package/dist/config.js +58 -0
  94. package/dist/config.js.map +1 -1
  95. package/dist/contact/index.d.ts +1 -1
  96. package/dist/contact/index.d.ts.map +1 -1
  97. package/dist/contact/index.js +1 -1
  98. package/dist/contact/index.js.map +1 -1
  99. package/dist/contact/records.d.ts +8 -2
  100. package/dist/contact/records.d.ts.map +1 -1
  101. package/dist/contact/records.js +13 -6
  102. package/dist/contact/records.js.map +1 -1
  103. package/dist/env.d.ts +25 -0
  104. package/dist/env.d.ts.map +1 -1
  105. package/dist/federation/delivery.d.ts +3 -1
  106. package/dist/federation/delivery.d.ts.map +1 -1
  107. package/dist/federation/delivery.js +6 -1
  108. package/dist/federation/delivery.js.map +1 -1
  109. package/dist/federation/federation.d.ts +3 -0
  110. package/dist/federation/federation.d.ts.map +1 -1
  111. package/dist/federation/federation.js.map +1 -1
  112. package/dist/federation/inbox.d.ts +7 -5
  113. package/dist/federation/inbox.d.ts.map +1 -1
  114. package/dist/federation/inbox.js +15 -32
  115. package/dist/federation/inbox.js.map +1 -1
  116. package/dist/federation/index.d.ts +2 -0
  117. package/dist/federation/index.d.ts.map +1 -1
  118. package/dist/federation/index.js +1 -0
  119. package/dist/federation/index.js.map +1 -1
  120. package/dist/federation/mount.js +6 -1
  121. package/dist/federation/mount.js.map +1 -1
  122. package/dist/federation/profiles.d.ts +95 -0
  123. package/dist/federation/profiles.d.ts.map +1 -0
  124. package/dist/federation/profiles.js +171 -0
  125. package/dist/federation/profiles.js.map +1 -0
  126. package/dist/federation/quotes.d.ts +2 -0
  127. package/dist/federation/quotes.d.ts.map +1 -1
  128. package/dist/federation/quotes.js +4 -0
  129. package/dist/federation/quotes.js.map +1 -1
  130. package/dist/federation/relays.d.ts +3 -1
  131. package/dist/federation/relays.d.ts.map +1 -1
  132. package/dist/federation/relays.js +6 -1
  133. package/dist/federation/relays.js.map +1 -1
  134. package/dist/federation/replies.d.ts +36 -4
  135. package/dist/federation/replies.d.ts.map +1 -1
  136. package/dist/federation/replies.js +71 -15
  137. package/dist/federation/replies.js.map +1 -1
  138. package/dist/images/icons.d.ts.map +1 -1
  139. package/dist/images/icons.js +3 -2
  140. package/dist/images/icons.js.map +1 -1
  141. package/dist/images/index.d.ts +2 -2
  142. package/dist/images/index.d.ts.map +1 -1
  143. package/dist/images/markup.d.ts +15 -4
  144. package/dist/images/markup.d.ts.map +1 -1
  145. package/dist/images/markup.js +24 -15
  146. package/dist/images/markup.js.map +1 -1
  147. package/dist/images/paths.d.ts +11 -0
  148. package/dist/images/paths.d.ts.map +1 -1
  149. package/dist/images/paths.js +17 -1
  150. package/dist/images/paths.js.map +1 -1
  151. package/dist/images/variants.d.ts +13 -3
  152. package/dist/images/variants.d.ts.map +1 -1
  153. package/dist/images/variants.js +13 -11
  154. package/dist/images/variants.js.map +1 -1
  155. package/dist/index.d.ts +34 -7
  156. package/dist/index.d.ts.map +1 -1
  157. package/dist/index.js +91 -18
  158. package/dist/index.js.map +1 -1
  159. package/dist/notifications/index.d.ts +1 -1
  160. package/dist/notifications/index.d.ts.map +1 -1
  161. package/dist/notifications/index.js +1 -1
  162. package/dist/notifications/index.js.map +1 -1
  163. package/dist/notifications/optouts.d.ts +6 -0
  164. package/dist/notifications/optouts.d.ts.map +1 -1
  165. package/dist/notifications/optouts.js +17 -0
  166. package/dist/notifications/optouts.js.map +1 -1
  167. package/dist/notifications/routes.js +1 -1
  168. package/dist/notifications/routes.js.map +1 -1
  169. package/dist/privacy/erase.d.ts +45 -0
  170. package/dist/privacy/erase.d.ts.map +1 -0
  171. package/dist/privacy/erase.js +76 -0
  172. package/dist/privacy/erase.js.map +1 -0
  173. package/dist/privacy/policy.d.ts +32 -0
  174. package/dist/privacy/policy.d.ts.map +1 -0
  175. package/dist/privacy/policy.js +37 -0
  176. package/dist/privacy/policy.js.map +1 -0
  177. package/dist/privacy/retention.d.ts +79 -0
  178. package/dist/privacy/retention.d.ts.map +1 -0
  179. package/dist/privacy/retention.js +133 -0
  180. package/dist/privacy/retention.js.map +1 -0
  181. package/dist/web/admin-bar.d.ts +21 -0
  182. package/dist/web/admin-bar.d.ts.map +1 -0
  183. package/dist/web/admin-bar.js +145 -0
  184. package/dist/web/admin-bar.js.map +1 -0
  185. package/dist/web/assets.d.ts +29 -1
  186. package/dist/web/assets.d.ts.map +1 -1
  187. package/dist/web/assets.js +48 -3
  188. package/dist/web/assets.js.map +1 -1
  189. package/dist/web/compression.d.ts +19 -0
  190. package/dist/web/compression.d.ts.map +1 -0
  191. package/dist/web/compression.js +160 -0
  192. package/dist/web/compression.js.map +1 -0
  193. package/dist/web/context.d.ts +5 -1
  194. package/dist/web/context.d.ts.map +1 -1
  195. package/dist/web/context.js +5 -2
  196. package/dist/web/context.js.map +1 -1
  197. package/dist/web/conversation.d.ts +9 -1
  198. package/dist/web/conversation.d.ts.map +1 -1
  199. package/dist/web/conversation.js +79 -17
  200. package/dist/web/conversation.js.map +1 -1
  201. package/dist/web/index.d.ts +1 -0
  202. package/dist/web/index.d.ts.map +1 -1
  203. package/dist/web/index.js +1 -0
  204. package/dist/web/index.js.map +1 -1
  205. package/dist/web/maintenance.d.ts.map +1 -1
  206. package/dist/web/maintenance.js +7 -2
  207. package/dist/web/maintenance.js.map +1 -1
  208. package/dist/web/render.d.ts +27 -0
  209. package/dist/web/render.d.ts.map +1 -1
  210. package/dist/web/render.js +77 -38
  211. package/dist/web/render.js.map +1 -1
  212. package/dist/web/routes.d.ts.map +1 -1
  213. package/dist/web/routes.js +67 -28
  214. package/dist/web/routes.js.map +1 -1
  215. package/dist/web/templates.d.ts.map +1 -1
  216. package/dist/web/templates.js +9 -2
  217. package/dist/web/templates.js.map +1 -1
  218. package/dist/web/well-known.d.ts +21 -0
  219. package/dist/web/well-known.d.ts.map +1 -0
  220. package/dist/web/well-known.js +66 -0
  221. package/dist/web/well-known.js.map +1 -0
  222. package/dist/webmention/fetch-public.d.ts +34 -0
  223. package/dist/webmention/fetch-public.d.ts.map +1 -0
  224. package/dist/webmention/fetch-public.js +101 -0
  225. package/dist/webmention/fetch-public.js.map +1 -0
  226. package/dist/webmention/reply-context.d.ts.map +1 -1
  227. package/dist/webmention/reply-context.js +12 -89
  228. package/dist/webmention/reply-context.js.map +1 -1
  229. package/dist/webmention/service.d.ts.map +1 -1
  230. package/dist/webmention/service.js +5 -1
  231. package/dist/webmention/service.js.map +1 -1
  232. package/package.json +1 -1
  233. package/templates/site/content/_data/site.json +7 -1
  234. package/templates/site/content/pages/privacy.md +127 -0
  235. package/themes/default/README.md +165 -22
  236. package/themes/default/layouts/base.njk +51 -10
@@ -1,4 +1,5 @@
1
- import { createReadStream, statSync } from 'node:fs';
1
+ import { createHash } from 'node:crypto';
2
+ import { createReadStream, readFileSync, statSync } from 'node:fs';
2
3
  import path from 'node:path';
3
4
  import { Readable } from 'node:stream';
4
5
  /** URL prefix the theme's own files are served under. */
@@ -21,8 +22,15 @@ export function findUpload(relative, contentDir) {
21
22
  }
22
23
  /** Directory inside a theme that holds the files served over HTTP. */
23
24
  export const THEME_STATIC_DIR = 'static';
24
- /** How long a browser may keep a theme asset. One hour. */
25
+ /** How long a browser may keep a theme asset at its unhashed URL. One hour. */
25
26
  export const THEME_ASSET_MAX_AGE = 3600;
27
+ /**
28
+ * How long a browser may keep a file whose URL names its bytes. A year, the
29
+ * longest lifetime HTTP caches are asked to honour.
30
+ */
31
+ export const IMMUTABLE_ASSET_MAX_AGE = 31536000;
32
+ /** The query parameter a fingerprinted theme asset URL carries its hash in. */
33
+ export const ASSET_VERSION_PARAM = 'v';
26
34
  /**
27
35
  * Find one file under a list of roots, first root that has it winning.
28
36
  *
@@ -69,6 +77,42 @@ export function findAsset(relative, roots) {
69
77
  export function findThemeAsset(relative, themeDirs) {
70
78
  return findAsset(relative, themeDirs.map((themeDir) => path.join(themeDir, THEME_STATIC_DIR)));
71
79
  }
80
+ /** The lifetime of a response whose URL names its bytes. */
81
+ export const IMMUTABLE_ASSET = {
82
+ maxAge: IMMUTABLE_ASSET_MAX_AGE,
83
+ immutable: true,
84
+ };
85
+ const versions = new Map();
86
+ /**
87
+ * A short hash of an asset's bytes, the part of a fingerprinted URL that
88
+ * changes when the file does.
89
+ *
90
+ * Memoised per file on the size-and-mtime validator, so a page pays a `stat`
91
+ * rather than a read, and an edit in place is seen on the next render with no
92
+ * restart and no watcher.
93
+ */
94
+ export function assetVersion(asset) {
95
+ const known = versions.get(asset.file);
96
+ if (known?.etag === asset.etag)
97
+ return known.version;
98
+ const version = createHash('sha256').update(readFileSync(asset.file)).digest('hex').slice(0, 12);
99
+ versions.set(asset.file, { etag: asset.etag, version });
100
+ return version;
101
+ }
102
+ /**
103
+ * The URL a theme should link one of its files at: under
104
+ * {@link THEME_ASSET_PREFIX} with a hash of the bytes the search path resolves
105
+ * to, so it can be cached for a year and still change the moment the file
106
+ * does. A file no theme has gets its plain URL, which will 404 as it would
107
+ * have anyway.
108
+ */
109
+ export function themeAssetUrl(relative, themeDirs) {
110
+ const pathname = `${THEME_ASSET_PREFIX}${relative.replace(/^\/+/, '')}`;
111
+ const asset = findThemeAsset(relative, themeDirs);
112
+ if (asset === undefined)
113
+ return pathname;
114
+ return `${pathname}?${ASSET_VERSION_PARAM}=${assetVersion(asset)}`;
115
+ }
72
116
  /** The response body and headers for an asset. */
73
117
  export function assetResponse(asset, options = {}) {
74
118
  const body = Readable.toWeb(createReadStream(asset.file));
@@ -102,10 +146,11 @@ export function matchesEtag(ifNoneMatch, etag) {
102
146
  }
103
147
  function assetHeaders(asset, options) {
104
148
  const maxAge = options.maxAge ?? THEME_ASSET_MAX_AGE;
149
+ const immutable = options.immutable === true ? ', immutable' : '';
105
150
  return new Headers({
106
151
  'content-type': asset.contentType,
107
152
  'content-length': String(asset.stats.size),
108
- 'cache-control': `public, max-age=${String(maxAge)}`,
153
+ 'cache-control': `public, max-age=${String(maxAge)}${immutable}`,
109
154
  'last-modified': new Date(asset.stats.mtimeMs).toUTCString(),
110
155
  etag: asset.etag,
111
156
  });
@@ -1 +1 @@
1
- {"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/web/assets.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/web/assets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { MiddlewareHandler } from 'hono';
2
+ /**
3
+ * Below this many bytes a response goes out as it is: the saving is smaller
4
+ * than the encoding's own framing and the CPU it costs.
5
+ */
6
+ export declare const COMPRESSION_THRESHOLD = 1024;
7
+ /**
8
+ * Compress text responses on the way out (TASK-139).
9
+ *
10
+ * Mounted outside every other middleware, so it sees the body the client is
11
+ * about to get. It adds `Vary: Accept-Encoding` to every text response,
12
+ * compressed or not, so a cache never hands brotli to a client that asked for
13
+ * none. A compressed response's ETag is weakened: the encoded octets differ
14
+ * from the plain ones, but the representation is the same, and every
15
+ * `If-None-Match` check in the CMS already compares weakly, so the one
16
+ * validator revalidates in every encoding.
17
+ */
18
+ export declare function compression(): MiddlewareHandler;
19
+ //# sourceMappingURL=compression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compression.d.ts","sourceRoot":"","sources":["../../src/web/compression.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAK9C;;;GAGG;AACH,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAsB1C;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,IAAI,iBAAiB,CAoD/C"}
@@ -0,0 +1,160 @@
1
+ import { promisify } from 'node:util';
2
+ import { brotliCompress, constants, gzip } from 'node:zlib';
3
+ import { ADMIN_ASSET_PREFIX } from '../admin/assets.js';
4
+ import { ADMIN_PREFIX } from '../admin/session.js';
5
+ /**
6
+ * Below this many bytes a response goes out as it is: the saving is smaller
7
+ * than the encoding's own framing and the CPU it costs.
8
+ */
9
+ export const COMPRESSION_THRESHOLD = 1024;
10
+ const ENCODINGS = ['br', 'gzip'];
11
+ /**
12
+ * Media types that are text under another name. Everything `text/*`, `+json`
13
+ * and `+xml` (feeds, ActivityStreams, SVG, the manifest) is text already; the
14
+ * fonts are the two formats that carry no compression of their own.
15
+ */
16
+ const COMPRESSIBLE_TYPES = new Set([
17
+ 'application/json',
18
+ 'application/javascript',
19
+ 'application/xml',
20
+ 'font/otf',
21
+ 'font/ttf',
22
+ ]);
23
+ const brotli = promisify(brotliCompress);
24
+ const gzipAsync = promisify(gzip);
25
+ /**
26
+ * Compress text responses on the way out (TASK-139).
27
+ *
28
+ * Mounted outside every other middleware, so it sees the body the client is
29
+ * about to get. It adds `Vary: Accept-Encoding` to every text response,
30
+ * compressed or not, so a cache never hands brotli to a client that asked for
31
+ * none. A compressed response's ETag is weakened: the encoded octets differ
32
+ * from the plain ones, but the representation is the same, and every
33
+ * `If-None-Match` check in the CMS already compares weakly, so the one
34
+ * validator revalidates in every encoding.
35
+ */
36
+ export function compression() {
37
+ return async (c, next) => {
38
+ await next();
39
+ const response = c.res;
40
+ if (response.status === 304) {
41
+ // A 304 carries no media type to judge by, and a cache copies its
42
+ // headers over the stored response's, so a 304 saying only `Vary:
43
+ // Accept` would strip Accept-Encoding from a stored page. Every 304
44
+ // says it varies: over-stating Vary costs a cache a spare entry, while
45
+ // under-stating it could hand brotli to a client that cannot read it.
46
+ c.res = new Response(null, response);
47
+ addVary(c.res.headers);
48
+ // A client revalidating a compressed copy sends the weak form it was
49
+ // given; hand that back, so the validator it stores stays the one it has.
50
+ const etag = c.res.headers.get('etag');
51
+ if (etag !== null &&
52
+ !etag.startsWith('W/') &&
53
+ presents(c.req.header('if-none-match'), `W/${etag}`)) {
54
+ c.res.headers.set('etag', `W/${etag}`);
55
+ }
56
+ return;
57
+ }
58
+ if (holdsSecrets(c.req.path, response) || !worthCompressing(response))
59
+ return;
60
+ const encoding = chooseEncoding(c.req.header('accept-encoding'));
61
+ const declared = Number(response.headers.get('content-length') ?? Number.NaN);
62
+ if (encoding === undefined || declared < COMPRESSION_THRESHOLD) {
63
+ c.res = new Response(response.body, response);
64
+ addVary(c.res.headers);
65
+ return;
66
+ }
67
+ const plain = new Uint8Array(await response.arrayBuffer());
68
+ if (plain.byteLength < COMPRESSION_THRESHOLD) {
69
+ c.res = new Response(plain, response);
70
+ addVary(c.res.headers);
71
+ return;
72
+ }
73
+ const encoded = await encode(plain, encoding);
74
+ c.res = new Response(encoded, response);
75
+ const headers = c.res.headers;
76
+ addVary(headers);
77
+ headers.set('content-encoding', encoding);
78
+ headers.set('content-length', String(encoded.byteLength));
79
+ const etag = headers.get('etag');
80
+ if (etag !== null && !etag.startsWith('W/'))
81
+ headers.set('etag', `W/${etag}`);
82
+ };
83
+ }
84
+ /**
85
+ * Whether a response may carry a secret beside text a visitor controls: a
86
+ * CSRF token, a session's own data. Compressing such a page lets an attacker
87
+ * who can inject text and watch its size recover the secret a byte at a time
88
+ * (BREACH), so it goes out plain. Every page drawn for somebody signed in says
89
+ * `private` or `no-store`; the admin's screens, the setup and login forms
90
+ * among them, say neither, so the path covers those. The admin's static files
91
+ * hold no secret and are compressed like any other.
92
+ */
93
+ function holdsSecrets(requestPath, response) {
94
+ if (/\b(private|no-store)\b/i.test(response.headers.get('cache-control') ?? ''))
95
+ return true;
96
+ if (requestPath.startsWith(ADMIN_ASSET_PREFIX))
97
+ return false;
98
+ return requestPath === ADMIN_PREFIX || requestPath.startsWith(`${ADMIN_PREFIX}/`);
99
+ }
100
+ /** Whether this response is text with a body that nothing has encoded yet. */
101
+ function worthCompressing(response) {
102
+ if (response.body === null || response.status === 204 || response.status === 206)
103
+ return false;
104
+ if (response.headers.has('content-encoding'))
105
+ return false;
106
+ if (/\bno-transform\b/i.test(response.headers.get('cache-control') ?? ''))
107
+ return false;
108
+ return isCompressible(response.headers.get('content-type'));
109
+ }
110
+ function isCompressible(contentType) {
111
+ const type = (contentType ?? '').split(';')[0]?.trim().toLowerCase() ?? '';
112
+ return (type.startsWith('text/') ||
113
+ type.endsWith('+json') ||
114
+ type.endsWith('+xml') ||
115
+ COMPRESSIBLE_TYPES.has(type));
116
+ }
117
+ /**
118
+ * The encoding to send for an `Accept-Encoding`, brotli first. A coding the
119
+ * header names with `q=0` is refused; one it leaves out is taken only when
120
+ * `*` is offered.
121
+ */
122
+ function chooseEncoding(accept) {
123
+ if (accept === undefined)
124
+ return undefined;
125
+ const weights = new Map();
126
+ for (const part of accept.split(',')) {
127
+ const [name = '', ...params] = part.split(';').map((piece) => piece.trim().toLowerCase());
128
+ if (name === '')
129
+ continue;
130
+ const q = params.find((param) => param.startsWith('q='));
131
+ weights.set(name, q === undefined ? 1 : Number(q.slice(2)) || 0);
132
+ }
133
+ return ENCODINGS.find((encoding) => (weights.get(encoding) ?? weights.get('*') ?? 0) > 0);
134
+ }
135
+ function encode(body, encoding) {
136
+ if (encoding === 'gzip')
137
+ return gzipAsync(body, { level: 6 });
138
+ // Quality 5 rather than the maximum 11: these bodies are built per request,
139
+ // and at 11 the time spent encoding outweighs the bytes it saves.
140
+ return brotli(body, {
141
+ params: {
142
+ [constants.BROTLI_PARAM_MODE]: constants.BROTLI_MODE_TEXT,
143
+ [constants.BROTLI_PARAM_QUALITY]: 5,
144
+ [constants.BROTLI_PARAM_SIZE_HINT]: body.byteLength,
145
+ },
146
+ });
147
+ }
148
+ function addVary(headers) {
149
+ const names = (headers.get('vary') ?? '')
150
+ .split(',')
151
+ .map((name) => name.trim())
152
+ .filter((name) => name !== '');
153
+ if (names.some((name) => name === '*' || name.toLowerCase() === 'accept-encoding'))
154
+ return;
155
+ headers.set('vary', [...names, 'Accept-Encoding'].join(', '));
156
+ }
157
+ function presents(ifNoneMatch, etag) {
158
+ return (ifNoneMatch ?? '').split(',').some((candidate) => candidate.trim() === etag);
159
+ }
160
+ //# sourceMappingURL=compression.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compression.js","sourceRoot":"","sources":["../../src/web/compression.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import type { ResolvedConfig } from '../config.ts';
2
2
  import type { Document } from '../content/document.ts';
3
3
  import type { PostType } from '../content/post-type.ts';
4
+ import type { ImageLoading } from '../images/markup.ts';
4
5
  import type { ImageConfig } from '../images/variants.ts';
5
6
  import type { AuthorContext } from './authors.ts';
6
7
  import type { TaxonomyBases, TaxonomyRedirect } from './taxonomy.ts';
@@ -189,8 +190,11 @@ export interface NeighbourContext {
189
190
  * login, a display name from before decision-14, or somebody who has no
190
191
  * account here at all. Without one the file's own string is left where it was,
191
192
  * which is what a test over a single template renders.
193
+ *
194
+ * `loading` says whether this document opens the page it is printed on, and
195
+ * so whether its first image is fetched at once rather than lazily.
192
196
  */
193
- export declare function documentContext(document: Document, images?: ImageConfig, author?: AuthorContext): DocumentContext;
197
+ export declare function documentContext(document: Document, images?: ImageConfig, author?: AuthorContext, loading?: ImageLoading): DocumentContext;
194
198
  /** Reads `content/_data/site.json`, and re-reads it when the file changes. */
195
199
  export interface SiteDataSource {
196
200
  /** The current site data, defaults filled in. */
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/web/context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/web/context.ts"],"names":[],"mappings":"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"}
@@ -31,8 +31,11 @@ export const DEFAULT_POSTS_PER_PAGE = 10;
31
31
  * login, a display name from before decision-14, or somebody who has no
32
32
  * account here at all. Without one the file's own string is left where it was,
33
33
  * which is what a test over a single template renders.
34
+ *
35
+ * `loading` says whether this document opens the page it is printed on, and
36
+ * so whether its first image is fetched at once rather than lazily.
34
37
  */
35
- export function documentContext(document, images, author) {
38
+ export function documentContext(document, images, author, loading) {
36
39
  const date = toDate(document.date);
37
40
  return {
38
41
  ...document.extra,
@@ -51,7 +54,7 @@ export function documentContext(document, images, author) {
51
54
  ...optional('date', date),
52
55
  tags: document.tags,
53
56
  categories: document.categories,
54
- content: images === undefined ? document.html : siteImageMarkup(images, document.html),
57
+ content: images === undefined ? document.html : siteImageMarkup(images, document.html, loading),
55
58
  // The plain-text summary, taken off the document rather than off the
56
59
  // markup above: an excerpt is text, and the `<picture>` a site's image
57
60
  // config puts in the HTML is not something to cut words out of.
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/web/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGtD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAGpG,8EAA8E;AAC9E,MAAM,CAAC,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAEhD,sEAAsE;AACtE,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAqKzC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAkB,EAClB,MAAoB,EACpB,MAAsB;IAEtB,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAEnC,OAAO;QACL,GAAG,QAAQ,CAAC,KAAK;QACjB,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,GAAG,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,WAAW,CAAC;QAChD,GAAG,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC;QAChD,GAAG,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChD,GAAG,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,WAAW,CAAC;QAEhD,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC;QAC9B,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC;QACxB,GAAG,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC/C,KAAK,EAAE,SAAS,CAAC,QAAQ,CAAC;QAC1B,GAAG,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;QACzB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,OAAO,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC;QACtF,qEAAqE;QACrE,uEAAuE;QACvE,gEAAgE;QAChE,OAAO,EAAE,WAAW,CAAC,QAAQ,CAAC;QAC9B,GAAG,EAAE,QAAQ,CAAC,SAAS;QACvB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,IAAI,EAAE;YACJ,GAAG,EAAE,QAAQ,CAAC,SAAS;YACvB,GAAG,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;YACzB,QAAQ,EAAE,QAAQ,CAAC,IAAI;YACvB,SAAS,EAAE,KAAK,QAAQ,CAAC,IAAI,EAAE;SAChC;KACF,CAAC;AACJ,CAAC;AAQD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAsB;IACzD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAExE,IAAI,MAAM,GAA4B,EAAE,CAAC;IACzC,IAAI,UAA8B,CAAC;IAEnC,SAAS,QAAQ;QACf,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,UAAU,GAAG,SAAS,CAAC;YACvB,MAAM,GAAG,EAAE,CAAC;YACZ,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,IAAI,IAAI,KAAK,UAAU;YAAE,OAAO,MAAM,CAAC;QAEvC,UAAU,GAAG,IAAI,CAAC;QAClB,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QAC7B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO;QACL,IAAI;YACF,kEAAkE;YAClE,sEAAsE;YACtE,mBAAmB;YACnB,MAAM,QAAQ,GAAa;gBACzB,KAAK,EAAE,SAAS;gBAChB,GAAG,EAAE,MAAM,CAAC,OAAO;gBACnB,OAAO,EAAE,sBAAsB,CAAC,GAAG;gBACnC,YAAY,EAAE,sBAAsB,CAAC,QAAQ;aAC9C,CAAC;YACF,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,QAAQ,EAAE,EAAE,CAAC;QACxC,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAC5E,CAAC,CAAE,MAAkC;YACrC,CAAC,CAAC,EAAE,CAAC;IACT,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAC,IAAc;IACzC,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;IACxC,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACrF,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,OAAO,sBAAsB,CAAC;AAChC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,OAAO,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACvE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,IAAc;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;IAClC,OAAO,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC;AACvF,CAAC;AAUD;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,IAAc;IAC3C,MAAM,QAAQ,GAAG,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACrF,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAExF,6EAA6E;IAC7E,yEAAyE;IACzE,oDAAoD;IACpD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AACnE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,IAAc;IAC1C,OAAO,sBAAsB,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;AAC1F,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAc;IAC1C,OAAO,mBAAmB,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,MAAM,CAAC,KAAyB;IACvC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/B,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;AAC7D,CAAC;AAED,SAAS,QAAQ,CAAsB,GAAM,EAAE,KAAoB;IACjE,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAmB,CAAC;AACvE,CAAC"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/web/context.ts"],"names":[],"mappings":"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"}
@@ -62,7 +62,10 @@ export interface InteractionAuthor {
62
62
  readonly handle: string | null;
63
63
  /** Their profile page, for a reader following the link. */
64
64
  readonly url: string | null;
65
- /** Their avatar, when the site knows one. */
65
+ /**
66
+ * Their avatar, when the site knows one: always a same-origin path, never
67
+ * the remote URL, so a reader's browser asks this site for it (TASK-134).
68
+ */
66
69
  readonly avatar: string | null;
67
70
  /** Their id, which is what identifies them however they are named. */
68
71
  readonly actorId: string | null;
@@ -130,6 +133,11 @@ export interface ConversationContext {
130
133
  * showing while the post it is about is there to read.
131
134
  */
132
135
  readonly store: ContentStore;
136
+ /**
137
+ * Where the quote approvals are: a quote of a post is shown only while the
138
+ * post's author vouches for it (FEP-044f, TASK-171).
139
+ */
140
+ readonly contentDir: string;
133
141
  /** The site's public origin, for the post's object id. */
134
142
  readonly baseUrl: string;
135
143
  }
@@ -1 +1 @@
1
- {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/web/conversation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/web/conversation.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,7 @@
1
+ import { avatarHref } from '../avatars/avatars.js';
1
2
  import { postLabel } from '../content/post-type.js';
2
- import { actorHandle, replyFrom, REPLY_ACTIVITY_TYPE } from '../federation/replies.js';
3
+ import { readAllQuoteAuthorizations } from '../federation/quotes.js';
4
+ import { actorHandle, guessedName, quoteFrom, replyFrom, REPLY_ACTIVITY_TYPE, } from '../federation/replies.js';
3
5
  import { activityStreamsId, isPublicDocument, permalinkOfObjectId } from './documents.js';
4
6
  import { absoluteUrl } from './negotiate.js';
5
7
  import { sanitizeCommentHtml } from './sanitize.js';
@@ -42,14 +44,17 @@ function postConversation(context, document) {
42
44
  const root = objectId ?? document.permalink;
43
45
  const activities = objectId === undefined ? [] : activitiesAround(context.admin, objectId);
44
46
  const native = approvedComments(context.admin, document);
45
- if (activities.length === 0 && native.length === 0)
46
- return NOTHING;
47
47
  const naming = authorNaming(context.admin);
48
+ const quotes = objectId === undefined
49
+ ? []
50
+ : quotesOf(context.admin, readAllQuoteAuthorizations(context.contentDir), objectId, naming);
51
+ if (activities.length === 0 && native.length === 0 && quotes.length === 0)
52
+ return NOTHING;
48
53
  const withdrawn = withdrawnBy(activities);
49
54
  const written = [];
50
55
  const likes = [];
51
56
  const boosts = [];
52
- const mentions = [];
57
+ const mentions = [...quotes];
53
58
  const reacted = new Set();
54
59
  for (const activity of activities) {
55
60
  const kind = REACTIONS[activity.activityType];
@@ -154,6 +159,16 @@ function siteConversation(context, limit) {
154
159
  break;
155
160
  }
156
161
  }
162
+ // Every quote the site vouches for, rather than a page of them: there is one
163
+ // approval per quote, and a site has few enough to read whole.
164
+ for (const approval of readAllQuoteAuthorizations(context.contentDir)) {
165
+ const post = posts.get(approval.post);
166
+ if (post === undefined)
167
+ continue;
168
+ for (const quote of quotesOf(context.admin, [approval], approval.post, naming)) {
169
+ said.push({ ...quote, post });
170
+ }
171
+ }
157
172
  for (const stored of context.admin.listComments({
158
173
  status: 'approved',
159
174
  limit: limit * OVERSCAN,
@@ -168,9 +183,14 @@ function siteConversation(context, limit) {
168
183
  /** How many answers each of these posts has, by permalink. */
169
184
  function commentCounts(context, documents) {
170
185
  const counts = new Map();
186
+ const approvals = readAllQuoteAuthorizations(context.contentDir);
187
+ const naming = authorNaming(context.admin);
171
188
  for (const document of documents) {
172
189
  const objectId = activityStreamsId(document, context.baseUrl);
173
- const federated = objectId === undefined ? 0 : context.admin.countRepliesTo(objectId);
190
+ const federated = objectId === undefined
191
+ ? 0
192
+ : context.admin.countRepliesTo(objectId) +
193
+ quotesOf(context.admin, approvals, objectId, naming).length;
174
194
  counts.set(document.permalink, federated + context.admin.countCommentsFor(document.slug, 'approved'));
175
195
  }
176
196
  return counts;
@@ -184,6 +204,46 @@ function commentCounts(context, documents) {
184
204
  * whose posts do not.
185
205
  */
186
206
  const OVERSCAN = 4;
207
+ /**
208
+ * The approved quotes of one post, as mentions, in no order.
209
+ *
210
+ * The approval is what makes a quote showable (FEP-044f: a quote its author
211
+ * never approved should not be displayed), so the approvals are walked and the
212
+ * log is asked for each one's note, rather than the other way about. The note
213
+ * has to be the approved one, by the approved actor, quoting this post: an
214
+ * approval of one quote vouches for nothing else. Both are files (decision-9),
215
+ * so a rebuilt index shows exactly what the live one did.
216
+ */
217
+ function quotesOf(admin, approvals, objectId, naming) {
218
+ const quotes = [];
219
+ for (const approval of approvals) {
220
+ if (approval.post !== objectId)
221
+ continue;
222
+ for (const activity of admin.listActivitiesAbout([approval.quote])) {
223
+ if (activity.actorId !== approval.actor)
224
+ continue;
225
+ const quote = quoteFrom(activity);
226
+ if (quote?.id !== approval.quote || quote.quoted !== objectId)
227
+ continue;
228
+ quotes.push({
229
+ id: quote.id,
230
+ source: 'activitypub',
231
+ kind: 'mention',
232
+ author: naming(quote.actorId),
233
+ url: quote.url,
234
+ content: sanitizeCommentHtml(quote.html),
235
+ published: quote.published,
236
+ inReplyTo: null,
237
+ status: 'published',
238
+ replies: [],
239
+ });
240
+ // The first `Create` of the note is the one shown: a redelivery is the
241
+ // same quote again.
242
+ break;
243
+ }
244
+ }
245
+ return quotes;
246
+ }
187
247
  /** One logged fediverse reply as the thread's own shape. */
188
248
  function federatedInteraction(reply, author) {
189
249
  return {
@@ -229,7 +289,7 @@ function interactionOf(comment, permalink) {
229
289
  url: comment.author.url,
230
290
  // Only a webmention has one: it came out of the source page's `h-card`,
231
291
  // and a form asks nobody for a picture.
232
- avatar: comment.author.avatar,
292
+ avatar: comment.author.avatar === null ? null : avatarHref(comment.author.avatar),
233
293
  actorId: null,
234
294
  },
235
295
  // Where it can be read. A comment written here lives here, at its own
@@ -494,10 +554,11 @@ function withdrawnBy(activities) {
494
554
  /**
495
555
  * How an actor is named, memoised for the length of one conversation.
496
556
  *
497
- * A follower is named from the profile it published when it followed; everyone
498
- * else is named by the handle their actor URL implies, which is a guess and
499
- * deliberately so — naming them properly would mean dereferencing the actor,
500
- * a network round trip per comment on the page.
557
+ * A follower is named from the profile it published when it followed, and
558
+ * anybody else from the profile the site fetched when they were first heard
559
+ * from (TASK-184). Both are read from the database: nothing is fetched while a
560
+ * page is drawn. Only an actor neither knows is named by a guess from their
561
+ * URL, which is their server when the URL ends in a number.
501
562
  */
502
563
  function authorNaming(admin) {
503
564
  const known = new Map();
@@ -505,19 +566,20 @@ function authorNaming(admin) {
505
566
  // followers are one row per (user, actor) now (decision-14), so the same
506
567
  // stranger may be on the list several times and any of those rows names them
507
568
  // the same way — the columns read here are the actor's own name and picture.
508
- let profiles;
569
+ let followers;
509
570
  return (actorId) => {
510
571
  const held = known.get(actorId);
511
572
  if (held !== undefined)
512
573
  return held;
513
- profiles ??= new Map(admin.listFollowers().map((entry) => [entry.actorId, entry]));
514
- const follower = profiles.get(actorId);
515
- const handle = follower?.handle ?? actorHandle(actorId) ?? null;
574
+ followers ??= new Map(admin.listFollowers().map((entry) => [entry.actorId, entry]));
575
+ const profile = followers.get(actorId) ?? admin.getActorProfile(actorId);
576
+ const handle = profile?.handle ?? actorHandle(actorId) ?? null;
577
+ const icon = profile?.iconUrl ?? null;
516
578
  const author = {
517
- name: follower?.name ?? handle ?? actorId,
579
+ name: profile?.name ?? handle ?? guessedName(actorId),
518
580
  handle,
519
- url: follower?.url ?? actorId,
520
- avatar: follower?.iconUrl ?? null,
581
+ url: profile?.url ?? actorId,
582
+ avatar: icon === null ? null : avatarHref(icon),
521
583
  actorId,
522
584
  };
523
585
  known.set(actorId, author);