@geekity/cms 0.3.0 → 0.4.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 (637) hide show
  1. package/README.md +2091 -214
  2. package/admin/components/fields.njk +165 -0
  3. package/admin/layouts/base.njk +1 -1
  4. package/admin/layouts/settings-page.njk +26 -0
  5. package/admin/layouts/shell.njk +17 -3
  6. package/admin/pages/account/forgot.njk +47 -0
  7. package/admin/pages/account/login.njk +26 -0
  8. package/admin/pages/account/reset.njk +38 -0
  9. package/admin/pages/account/setup.njk +28 -0
  10. package/admin/pages/appearance/themes.njk +69 -0
  11. package/admin/pages/comments/all.njk +133 -0
  12. package/admin/{layouts/dashboard.njk → pages/dashboard/home.njk} +8 -15
  13. package/admin/{layouts/document-conflict.njk → pages/documents/conflict.njk} +1 -0
  14. package/admin/pages/documents/editor.njk +196 -0
  15. package/admin/{layouts/document-list.njk → pages/documents/list.njk} +19 -2
  16. package/admin/pages/documents/taxonomy.njk +95 -0
  17. package/admin/pages/federation/followers.njk +238 -0
  18. package/admin/pages/media/library.njk +160 -0
  19. package/admin/pages/messages/all.njk +110 -0
  20. package/admin/{layouts → pages}/placeholder.njk +5 -1
  21. package/admin/pages/settings/discussion.njk +99 -0
  22. package/admin/pages/settings/email.njk +203 -0
  23. package/admin/pages/settings/federation.njk +75 -0
  24. package/admin/pages/settings/general.njk +29 -0
  25. package/admin/pages/settings/permalinks.njk +50 -0
  26. package/admin/pages/settings/reading.njk +72 -0
  27. package/admin/pages/users/edit.njk +239 -0
  28. package/admin/pages/users/list.njk +48 -0
  29. package/admin/pages/users/new.njk +41 -0
  30. package/admin/static/admin.css +498 -3
  31. package/admin/static/copy.js +52 -0
  32. package/admin/static/editor.js +16 -16
  33. package/admin/static/slug.js +61 -0
  34. package/dist/admin/accounts.d.ts +413 -0
  35. package/dist/admin/accounts.d.ts.map +1 -0
  36. package/dist/admin/accounts.js +680 -0
  37. package/dist/admin/accounts.js.map +1 -0
  38. package/dist/admin/appearance.d.ts +80 -0
  39. package/dist/admin/appearance.d.ts.map +1 -0
  40. package/dist/admin/appearance.js +136 -0
  41. package/dist/admin/appearance.js.map +1 -0
  42. package/dist/admin/comments.d.ts +90 -0
  43. package/dist/admin/comments.d.ts.map +1 -0
  44. package/dist/admin/comments.js +244 -0
  45. package/dist/admin/comments.js.map +1 -0
  46. package/dist/admin/credentials.d.ts +26 -2
  47. package/dist/admin/credentials.d.ts.map +1 -1
  48. package/dist/admin/credentials.js +35 -2
  49. package/dist/admin/credentials.js.map +1 -1
  50. package/dist/admin/documents.d.ts +127 -10
  51. package/dist/admin/documents.d.ts.map +1 -1
  52. package/dist/admin/documents.js +360 -41
  53. package/dist/admin/documents.js.map +1 -1
  54. package/dist/admin/federation.d.ts +151 -50
  55. package/dist/admin/federation.d.ts.map +1 -1
  56. package/dist/admin/federation.js +225 -93
  57. package/dist/admin/federation.js.map +1 -1
  58. package/dist/admin/formatting.d.ts +20 -0
  59. package/dist/admin/formatting.d.ts.map +1 -0
  60. package/dist/admin/formatting.js +72 -0
  61. package/dist/admin/formatting.js.map +1 -0
  62. package/dist/admin/headers.d.ts +65 -0
  63. package/dist/admin/headers.d.ts.map +1 -0
  64. package/dist/admin/headers.js +104 -0
  65. package/dist/admin/headers.js.map +1 -0
  66. package/dist/admin/index.d.ts +38 -12
  67. package/dist/admin/index.d.ts.map +1 -1
  68. package/dist/admin/index.js +25 -7
  69. package/dist/admin/index.js.map +1 -1
  70. package/dist/admin/media.d.ts +168 -0
  71. package/dist/admin/media.d.ts.map +1 -0
  72. package/dist/admin/media.js +295 -0
  73. package/dist/admin/media.js.map +1 -0
  74. package/dist/admin/menu.d.ts +79 -0
  75. package/dist/admin/menu.d.ts.map +1 -0
  76. package/dist/admin/menu.js +126 -0
  77. package/dist/admin/menu.js.map +1 -0
  78. package/dist/admin/messages.d.ts +80 -0
  79. package/dist/admin/messages.d.ts.map +1 -0
  80. package/dist/admin/messages.js +152 -0
  81. package/dist/admin/messages.js.map +1 -0
  82. package/dist/admin/preview.d.ts.map +1 -1
  83. package/dist/admin/preview.js +31 -7
  84. package/dist/admin/preview.js.map +1 -1
  85. package/dist/admin/recovery.d.ts +92 -0
  86. package/dist/admin/recovery.d.ts.map +1 -0
  87. package/dist/admin/recovery.js +229 -0
  88. package/dist/admin/recovery.js.map +1 -0
  89. package/dist/admin/routes.d.ts +0 -20
  90. package/dist/admin/routes.d.ts.map +1 -1
  91. package/dist/admin/routes.js +155 -105
  92. package/dist/admin/routes.js.map +1 -1
  93. package/dist/admin/settings-discussion.d.ts +37 -0
  94. package/dist/admin/settings-discussion.d.ts.map +1 -0
  95. package/dist/admin/settings-discussion.js +117 -0
  96. package/dist/admin/settings-discussion.js.map +1 -0
  97. package/dist/admin/settings-email.d.ts +54 -0
  98. package/dist/admin/settings-email.d.ts.map +1 -0
  99. package/dist/admin/settings-email.js +189 -0
  100. package/dist/admin/settings-email.js.map +1 -0
  101. package/dist/admin/settings-federation.d.ts +39 -0
  102. package/dist/admin/settings-federation.d.ts.map +1 -0
  103. package/dist/admin/settings-federation.js +103 -0
  104. package/dist/admin/settings-federation.js.map +1 -0
  105. package/dist/admin/settings-general.d.ts +12 -0
  106. package/dist/admin/settings-general.d.ts.map +1 -0
  107. package/dist/admin/settings-general.js +48 -0
  108. package/dist/admin/settings-general.js.map +1 -0
  109. package/dist/admin/settings-page.d.ts +102 -0
  110. package/dist/admin/settings-page.d.ts.map +1 -0
  111. package/dist/admin/settings-page.js +126 -0
  112. package/dist/admin/settings-page.js.map +1 -0
  113. package/dist/admin/settings-pages.d.ts +19 -0
  114. package/dist/admin/settings-pages.d.ts.map +1 -0
  115. package/dist/admin/settings-pages.js +33 -0
  116. package/dist/admin/settings-pages.js.map +1 -0
  117. package/dist/admin/settings-permalinks.d.ts +16 -0
  118. package/dist/admin/settings-permalinks.d.ts.map +1 -0
  119. package/dist/admin/settings-permalinks.js +32 -0
  120. package/dist/admin/settings-permalinks.js.map +1 -0
  121. package/dist/admin/settings-reading.d.ts +33 -0
  122. package/dist/admin/settings-reading.d.ts.map +1 -0
  123. package/dist/admin/settings-reading.js +48 -0
  124. package/dist/admin/settings-reading.js.map +1 -0
  125. package/dist/admin/settings.d.ts +325 -96
  126. package/dist/admin/settings.d.ts.map +1 -1
  127. package/dist/admin/settings.js +650 -308
  128. package/dist/admin/settings.js.map +1 -1
  129. package/dist/admin/store.d.ts +606 -152
  130. package/dist/admin/store.d.ts.map +1 -1
  131. package/dist/admin/store.js +863 -268
  132. package/dist/admin/store.js.map +1 -1
  133. package/dist/admin/taxonomy.d.ts +114 -0
  134. package/dist/admin/taxonomy.d.ts.map +1 -0
  135. package/dist/admin/taxonomy.js +289 -0
  136. package/dist/admin/taxonomy.js.map +1 -0
  137. package/dist/admin/templates.d.ts +44 -14
  138. package/dist/admin/templates.d.ts.map +1 -1
  139. package/dist/admin/templates.js +44 -14
  140. package/dist/admin/templates.js.map +1 -1
  141. package/dist/admin/throttle.d.ts +84 -0
  142. package/dist/admin/throttle.d.ts.map +1 -0
  143. package/dist/admin/throttle.js +143 -0
  144. package/dist/admin/throttle.js.map +1 -0
  145. package/dist/admin/uploads.d.ts +28 -2
  146. package/dist/admin/uploads.d.ts.map +1 -1
  147. package/dist/admin/uploads.js +43 -4
  148. package/dist/admin/uploads.js.map +1 -1
  149. package/dist/admin/users.d.ts +100 -13
  150. package/dist/admin/users.d.ts.map +1 -1
  151. package/dist/admin/users.js +412 -39
  152. package/dist/admin/users.js.map +1 -1
  153. package/dist/cache.d.ts +141 -0
  154. package/dist/cache.d.ts.map +1 -0
  155. package/dist/cache.js +207 -0
  156. package/dist/cache.js.map +1 -0
  157. package/dist/cli.d.ts +14 -2
  158. package/dist/cli.d.ts.map +1 -1
  159. package/dist/cli.js +424 -21
  160. package/dist/cli.js.map +1 -1
  161. package/dist/comments/akismet.d.ts +130 -0
  162. package/dist/comments/akismet.d.ts.map +1 -0
  163. package/dist/comments/akismet.js +274 -0
  164. package/dist/comments/akismet.js.map +1 -0
  165. package/dist/comments/conversation.d.ts +29 -0
  166. package/dist/comments/conversation.d.ts.map +1 -0
  167. package/dist/comments/conversation.js +60 -0
  168. package/dist/comments/conversation.js.map +1 -0
  169. package/dist/comments/form.d.ts +75 -0
  170. package/dist/comments/form.d.ts.map +1 -0
  171. package/dist/comments/form.js +64 -0
  172. package/dist/comments/form.js.map +1 -0
  173. package/dist/comments/index.d.ts +15 -0
  174. package/dist/comments/index.d.ts.map +1 -0
  175. package/dist/comments/index.js +9 -0
  176. package/dist/comments/index.js.map +1 -0
  177. package/dist/comments/markdown.d.ts +8 -0
  178. package/dist/comments/markdown.d.ts.map +1 -0
  179. package/dist/comments/markdown.js +85 -0
  180. package/dist/comments/markdown.js.map +1 -0
  181. package/dist/comments/moderate.d.ts +71 -0
  182. package/dist/comments/moderate.d.ts.map +1 -0
  183. package/dist/comments/moderate.js +88 -0
  184. package/dist/comments/moderate.js.map +1 -0
  185. package/dist/comments/policy.d.ts +57 -0
  186. package/dist/comments/policy.d.ts.map +1 -0
  187. package/dist/comments/policy.js +80 -0
  188. package/dist/comments/policy.js.map +1 -0
  189. package/dist/comments/records.d.ts +266 -0
  190. package/dist/comments/records.d.ts.map +1 -0
  191. package/dist/comments/records.js +487 -0
  192. package/dist/comments/records.js.map +1 -0
  193. package/dist/comments/routes.d.ts +58 -0
  194. package/dist/comments/routes.d.ts.map +1 -0
  195. package/dist/comments/routes.js +177 -0
  196. package/dist/comments/routes.js.map +1 -0
  197. package/dist/comments/submission.d.ts +263 -0
  198. package/dist/comments/submission.d.ts.map +1 -0
  199. package/dist/comments/submission.js +241 -0
  200. package/dist/comments/submission.js.map +1 -0
  201. package/dist/config.d.ts +190 -5
  202. package/dist/config.d.ts.map +1 -1
  203. package/dist/config.js +118 -6
  204. package/dist/config.js.map +1 -1
  205. package/dist/contact/delivery.d.ts +60 -0
  206. package/dist/contact/delivery.d.ts.map +1 -0
  207. package/dist/contact/delivery.js +87 -0
  208. package/dist/contact/delivery.js.map +1 -0
  209. package/dist/contact/form.d.ts +130 -0
  210. package/dist/contact/form.d.ts.map +1 -0
  211. package/dist/contact/form.js +163 -0
  212. package/dist/contact/form.js.map +1 -0
  213. package/dist/contact/index.d.ts +17 -0
  214. package/dist/contact/index.d.ts.map +1 -0
  215. package/dist/contact/index.js +13 -0
  216. package/dist/contact/index.js.map +1 -0
  217. package/dist/contact/records.d.ts +124 -0
  218. package/dist/contact/records.d.ts.map +1 -0
  219. package/dist/contact/records.js +218 -0
  220. package/dist/contact/records.js.map +1 -0
  221. package/dist/contact/routes.d.ts +17 -0
  222. package/dist/contact/routes.d.ts.map +1 -0
  223. package/dist/contact/routes.js +127 -0
  224. package/dist/contact/routes.js.map +1 -0
  225. package/dist/contact/submission.d.ts +103 -0
  226. package/dist/contact/submission.d.ts.map +1 -0
  227. package/dist/contact/submission.js +166 -0
  228. package/dist/contact/submission.js.map +1 -0
  229. package/dist/content/document.d.ts +7 -2
  230. package/dist/content/document.d.ts.map +1 -1
  231. package/dist/content/document.js +1 -0
  232. package/dist/content/document.js.map +1 -1
  233. package/dist/content/index.d.ts +7 -2
  234. package/dist/content/index.d.ts.map +1 -1
  235. package/dist/content/index.js +4 -1
  236. package/dist/content/index.js.map +1 -1
  237. package/dist/content/markdown.d.ts.map +1 -1
  238. package/dist/content/markdown.js +24 -2
  239. package/dist/content/markdown.js.map +1 -1
  240. package/dist/content/parser.d.ts.map +1 -1
  241. package/dist/content/parser.js +11 -4
  242. package/dist/content/parser.js.map +1 -1
  243. package/dist/content/save.d.ts +5 -0
  244. package/dist/content/save.d.ts.map +1 -1
  245. package/dist/content/save.js +44 -1
  246. package/dist/content/save.js.map +1 -1
  247. package/dist/content/schedule.d.ts +125 -0
  248. package/dist/content/schedule.d.ts.map +1 -0
  249. package/dist/content/schedule.js +207 -0
  250. package/dist/content/schedule.js.map +1 -0
  251. package/dist/content/search.d.ts +64 -0
  252. package/dist/content/search.d.ts.map +1 -0
  253. package/dist/content/search.js +136 -0
  254. package/dist/content/search.js.map +1 -0
  255. package/dist/content/store.d.ts +202 -7
  256. package/dist/content/store.d.ts.map +1 -1
  257. package/dist/content/store.js +374 -67
  258. package/dist/content/store.js.map +1 -1
  259. package/dist/content/sync.d.ts +7 -1
  260. package/dist/content/sync.d.ts.map +1 -1
  261. package/dist/content/sync.js +18 -10
  262. package/dist/content/sync.js.map +1 -1
  263. package/dist/content/time.d.ts +56 -0
  264. package/dist/content/time.d.ts.map +1 -0
  265. package/dist/content/time.js +141 -0
  266. package/dist/content/time.js.map +1 -0
  267. package/dist/content/writer.d.ts.map +1 -1
  268. package/dist/content/writer.js +3 -0
  269. package/dist/content/writer.js.map +1 -1
  270. package/dist/env.d.ts +58 -4
  271. package/dist/env.d.ts.map +1 -1
  272. package/dist/federation/actor.d.ts +95 -41
  273. package/dist/federation/actor.d.ts.map +1 -1
  274. package/dist/federation/actor.js +180 -53
  275. package/dist/federation/actor.js.map +1 -1
  276. package/dist/federation/article.d.ts +44 -17
  277. package/dist/federation/article.d.ts.map +1 -1
  278. package/dist/federation/article.js +98 -46
  279. package/dist/federation/article.js.map +1 -1
  280. package/dist/federation/delivery.d.ts +63 -15
  281. package/dist/federation/delivery.d.ts.map +1 -1
  282. package/dist/federation/delivery.js +154 -80
  283. package/dist/federation/delivery.js.map +1 -1
  284. package/dist/federation/federation.d.ts +22 -17
  285. package/dist/federation/federation.d.ts.map +1 -1
  286. package/dist/federation/federation.js +98 -77
  287. package/dist/federation/federation.js.map +1 -1
  288. package/dist/federation/followers.d.ts +3 -2
  289. package/dist/federation/followers.d.ts.map +1 -1
  290. package/dist/federation/followers.js +6 -5
  291. package/dist/federation/followers.js.map +1 -1
  292. package/dist/federation/import-wordpress.d.ts +128 -0
  293. package/dist/federation/import-wordpress.d.ts.map +1 -0
  294. package/dist/federation/import-wordpress.js +426 -0
  295. package/dist/federation/import-wordpress.js.map +1 -0
  296. package/dist/federation/inbox.d.ts +36 -13
  297. package/dist/federation/inbox.d.ts.map +1 -1
  298. package/dist/federation/inbox.js +120 -49
  299. package/dist/federation/inbox.js.map +1 -1
  300. package/dist/federation/index.d.ts +22 -10
  301. package/dist/federation/index.d.ts.map +1 -1
  302. package/dist/federation/index.js +13 -8
  303. package/dist/federation/index.js.map +1 -1
  304. package/dist/federation/keys.d.ts +98 -20
  305. package/dist/federation/keys.d.ts.map +1 -1
  306. package/dist/federation/keys.js +269 -39
  307. package/dist/federation/keys.js.map +1 -1
  308. package/dist/federation/mount.d.ts +42 -10
  309. package/dist/federation/mount.d.ts.map +1 -1
  310. package/dist/federation/mount.js +266 -26
  311. package/dist/federation/mount.js.map +1 -1
  312. package/dist/federation/paths.d.ts +23 -35
  313. package/dist/federation/paths.d.ts.map +1 -1
  314. package/dist/federation/paths.js +26 -39
  315. package/dist/federation/paths.js.map +1 -1
  316. package/dist/federation/records.d.ts +223 -0
  317. package/dist/federation/records.d.ts.map +1 -0
  318. package/dist/federation/records.js +506 -0
  319. package/dist/federation/records.js.map +1 -0
  320. package/dist/federation/relays.d.ts +114 -0
  321. package/dist/federation/relays.d.ts.map +1 -0
  322. package/dist/federation/relays.js +290 -0
  323. package/dist/federation/relays.js.map +1 -0
  324. package/dist/federation/replies.d.ts +80 -0
  325. package/dist/federation/replies.d.ts.map +1 -0
  326. package/dist/federation/replies.js +166 -0
  327. package/dist/federation/replies.js.map +1 -0
  328. package/dist/federation/wordpress.d.ts +131 -0
  329. package/dist/federation/wordpress.d.ts.map +1 -0
  330. package/dist/federation/wordpress.js +341 -0
  331. package/dist/federation/wordpress.js.map +1 -0
  332. package/dist/files/atomic.d.ts +103 -0
  333. package/dist/files/atomic.d.ts.map +1 -0
  334. package/dist/files/atomic.js +150 -0
  335. package/dist/files/atomic.js.map +1 -0
  336. package/dist/files/index.d.ts +3 -0
  337. package/dist/files/index.d.ts.map +1 -0
  338. package/dist/files/index.js +2 -0
  339. package/dist/files/index.js.map +1 -0
  340. package/dist/forms/protection.d.ts +85 -0
  341. package/dist/forms/protection.d.ts.map +1 -0
  342. package/dist/forms/protection.js +116 -0
  343. package/dist/forms/protection.js.map +1 -0
  344. package/dist/images/icons.d.ts +59 -0
  345. package/dist/images/icons.d.ts.map +1 -0
  346. package/dist/images/icons.js +131 -0
  347. package/dist/images/icons.js.map +1 -0
  348. package/dist/images/index.d.ts +7 -0
  349. package/dist/images/index.d.ts.map +1 -0
  350. package/dist/images/index.js +4 -0
  351. package/dist/images/index.js.map +1 -0
  352. package/dist/images/markup.d.ts +47 -0
  353. package/dist/images/markup.d.ts.map +1 -0
  354. package/dist/images/markup.js +175 -0
  355. package/dist/images/markup.js.map +1 -0
  356. package/dist/images/paths.d.ts +34 -0
  357. package/dist/images/paths.d.ts.map +1 -0
  358. package/dist/images/paths.js +41 -0
  359. package/dist/images/paths.js.map +1 -0
  360. package/dist/images/variants.d.ts +104 -0
  361. package/dist/images/variants.d.ts.map +1 -0
  362. package/dist/images/variants.js +320 -0
  363. package/dist/images/variants.js.map +1 -0
  364. package/dist/index.d.ts +123 -19
  365. package/dist/index.d.ts.map +1 -1
  366. package/dist/index.js +327 -25
  367. package/dist/index.js.map +1 -1
  368. package/dist/init.d.ts +18 -0
  369. package/dist/init.d.ts.map +1 -1
  370. package/dist/init.js +34 -3
  371. package/dist/init.js.map +1 -1
  372. package/dist/mail/brevo.d.ts +35 -0
  373. package/dist/mail/brevo.d.ts.map +1 -0
  374. package/dist/mail/brevo.js +85 -0
  375. package/dist/mail/brevo.js.map +1 -0
  376. package/dist/mail/credentials.d.ts +62 -0
  377. package/dist/mail/credentials.d.ts.map +1 -0
  378. package/dist/mail/credentials.js +86 -0
  379. package/dist/mail/credentials.js.map +1 -0
  380. package/dist/mail/index.d.ts +15 -0
  381. package/dist/mail/index.d.ts.map +1 -0
  382. package/dist/mail/index.js +8 -0
  383. package/dist/mail/index.js.map +1 -0
  384. package/dist/mail/memory.d.ts +35 -0
  385. package/dist/mail/memory.d.ts.map +1 -0
  386. package/dist/mail/memory.js +29 -0
  387. package/dist/mail/memory.js.map +1 -0
  388. package/dist/mail/provider.d.ts +80 -0
  389. package/dist/mail/provider.d.ts.map +1 -0
  390. package/dist/mail/provider.js +23 -0
  391. package/dist/mail/provider.js.map +1 -0
  392. package/dist/mail/service.d.ts +156 -0
  393. package/dist/mail/service.d.ts.map +1 -0
  394. package/dist/mail/service.js +265 -0
  395. package/dist/mail/service.js.map +1 -0
  396. package/dist/mail/smtp.d.ts +39 -0
  397. package/dist/mail/smtp.d.ts.map +1 -0
  398. package/dist/mail/smtp.js +58 -0
  399. package/dist/mail/smtp.js.map +1 -0
  400. package/dist/mail/templates.d.ts +91 -0
  401. package/dist/mail/templates.d.ts.map +1 -0
  402. package/dist/mail/templates.js +69 -0
  403. package/dist/mail/templates.js.map +1 -0
  404. package/dist/notifications/comments.d.ts +66 -0
  405. package/dist/notifications/comments.d.ts.map +1 -0
  406. package/dist/notifications/comments.js +151 -0
  407. package/dist/notifications/comments.js.map +1 -0
  408. package/dist/notifications/digest.d.ts +135 -0
  409. package/dist/notifications/digest.d.ts.map +1 -0
  410. package/dist/notifications/digest.js +287 -0
  411. package/dist/notifications/digest.js.map +1 -0
  412. package/dist/notifications/index.d.ts +13 -0
  413. package/dist/notifications/index.d.ts.map +1 -0
  414. package/dist/notifications/index.js +8 -0
  415. package/dist/notifications/index.js.map +1 -0
  416. package/dist/notifications/links.d.ts +60 -0
  417. package/dist/notifications/links.d.ts.map +1 -0
  418. package/dist/notifications/links.js +63 -0
  419. package/dist/notifications/links.js.map +1 -0
  420. package/dist/notifications/optouts.d.ts +48 -0
  421. package/dist/notifications/optouts.d.ts.map +1 -0
  422. package/dist/notifications/optouts.js +93 -0
  423. package/dist/notifications/optouts.js.map +1 -0
  424. package/dist/notifications/preferences.d.ts +158 -0
  425. package/dist/notifications/preferences.d.ts.map +1 -0
  426. package/dist/notifications/preferences.js +195 -0
  427. package/dist/notifications/preferences.js.map +1 -0
  428. package/dist/notifications/routes.d.ts +28 -0
  429. package/dist/notifications/routes.d.ts.map +1 -0
  430. package/dist/notifications/routes.js +184 -0
  431. package/dist/notifications/routes.js.map +1 -0
  432. package/dist/notifications/tokens.d.ts +60 -0
  433. package/dist/notifications/tokens.d.ts.map +1 -0
  434. package/dist/notifications/tokens.js +125 -0
  435. package/dist/notifications/tokens.js.map +1 -0
  436. package/dist/notify.d.ts +84 -0
  437. package/dist/notify.d.ts.map +1 -0
  438. package/dist/notify.js +149 -0
  439. package/dist/notify.js.map +1 -0
  440. package/dist/web/archive.d.ts +49 -0
  441. package/dist/web/archive.d.ts.map +1 -0
  442. package/dist/web/archive.js +59 -0
  443. package/dist/web/archive.js.map +1 -0
  444. package/dist/web/assets.d.ts +6 -5
  445. package/dist/web/assets.d.ts.map +1 -1
  446. package/dist/web/assets.js +5 -7
  447. package/dist/web/assets.js.map +1 -1
  448. package/dist/web/authors.d.ts +143 -0
  449. package/dist/web/authors.d.ts.map +1 -0
  450. package/dist/web/authors.js +178 -0
  451. package/dist/web/authors.js.map +1 -0
  452. package/dist/web/comments.d.ts +60 -0
  453. package/dist/web/comments.d.ts.map +1 -0
  454. package/dist/web/comments.js +207 -0
  455. package/dist/web/comments.js.map +1 -0
  456. package/dist/web/context.d.ts +162 -28
  457. package/dist/web/context.d.ts.map +1 -1
  458. package/dist/web/context.js +123 -26
  459. package/dist/web/context.js.map +1 -1
  460. package/dist/web/conversation.d.ts +209 -0
  461. package/dist/web/conversation.d.ts.map +1 -0
  462. package/dist/web/conversation.js +557 -0
  463. package/dist/web/conversation.js.map +1 -0
  464. package/dist/web/documents.d.ts +36 -6
  465. package/dist/web/documents.d.ts.map +1 -1
  466. package/dist/web/documents.js +52 -10
  467. package/dist/web/documents.js.map +1 -1
  468. package/dist/web/feed-atom.d.ts +22 -0
  469. package/dist/web/feed-atom.d.ts.map +1 -0
  470. package/dist/web/feed-atom.js +83 -0
  471. package/dist/web/feed-atom.js.map +1 -0
  472. package/dist/web/feed-item.d.ts +145 -0
  473. package/dist/web/feed-item.d.ts.map +1 -0
  474. package/dist/web/feed-item.js +120 -0
  475. package/dist/web/feed-item.js.map +1 -0
  476. package/dist/web/feed-json.d.ts +79 -0
  477. package/dist/web/feed-json.d.ts.map +1 -0
  478. package/dist/web/feed-json.js +53 -0
  479. package/dist/web/feed-json.js.map +1 -0
  480. package/dist/web/feed-rss.d.ts +26 -0
  481. package/dist/web/feed-rss.d.ts.map +1 -0
  482. package/dist/web/feed-rss.js +203 -0
  483. package/dist/web/feed-rss.js.map +1 -0
  484. package/dist/web/feed-source.d.ts +244 -0
  485. package/dist/web/feed-source.d.ts.map +1 -0
  486. package/dist/web/feed-source.js +201 -0
  487. package/dist/web/feed-source.js.map +1 -0
  488. package/dist/web/feed-xml.d.ts +63 -0
  489. package/dist/web/feed-xml.d.ts.map +1 -0
  490. package/dist/web/feed-xml.js +116 -0
  491. package/dist/web/feed-xml.js.map +1 -0
  492. package/dist/web/feeds.d.ts +31 -96
  493. package/dist/web/feeds.d.ts.map +1 -1
  494. package/dist/web/feeds.js +94 -198
  495. package/dist/web/feeds.js.map +1 -1
  496. package/dist/web/health.d.ts +31 -0
  497. package/dist/web/health.d.ts.map +1 -0
  498. package/dist/web/health.js +44 -0
  499. package/dist/web/health.js.map +1 -0
  500. package/dist/web/index.d.ts +30 -10
  501. package/dist/web/index.d.ts.map +1 -1
  502. package/dist/web/index.js +18 -7
  503. package/dist/web/index.js.map +1 -1
  504. package/dist/web/navigation.d.ts +73 -0
  505. package/dist/web/navigation.d.ts.map +1 -0
  506. package/dist/web/navigation.js +110 -0
  507. package/dist/web/navigation.js.map +1 -0
  508. package/dist/web/negotiate.d.ts +9 -0
  509. package/dist/web/negotiate.d.ts.map +1 -1
  510. package/dist/web/negotiate.js +6 -1
  511. package/dist/web/negotiate.js.map +1 -1
  512. package/dist/web/recent.d.ts +43 -0
  513. package/dist/web/recent.d.ts.map +1 -0
  514. package/dist/web/recent.js +37 -0
  515. package/dist/web/recent.js.map +1 -0
  516. package/dist/web/render.d.ts +197 -9
  517. package/dist/web/render.d.ts.map +1 -1
  518. package/dist/web/render.js +334 -17
  519. package/dist/web/render.js.map +1 -1
  520. package/dist/web/routes.d.ts +25 -8
  521. package/dist/web/routes.d.ts.map +1 -1
  522. package/dist/web/routes.js +845 -93
  523. package/dist/web/routes.js.map +1 -1
  524. package/dist/web/sanitize.d.ts +19 -0
  525. package/dist/web/sanitize.d.ts.map +1 -0
  526. package/dist/web/sanitize.js +272 -0
  527. package/dist/web/sanitize.js.map +1 -0
  528. package/dist/web/search.d.ts +79 -0
  529. package/dist/web/search.d.ts.map +1 -0
  530. package/dist/web/search.js +94 -0
  531. package/dist/web/search.js.map +1 -0
  532. package/dist/web/sitemap.d.ts +120 -0
  533. package/dist/web/sitemap.d.ts.map +1 -0
  534. package/dist/web/sitemap.js +199 -0
  535. package/dist/web/sitemap.js.map +1 -0
  536. package/dist/web/taxonomy.d.ts +152 -0
  537. package/dist/web/taxonomy.d.ts.map +1 -0
  538. package/dist/web/taxonomy.js +237 -0
  539. package/dist/web/taxonomy.js.map +1 -0
  540. package/dist/web/templates.d.ts +54 -16
  541. package/dist/web/templates.d.ts.map +1 -1
  542. package/dist/web/templates.js +109 -28
  543. package/dist/web/templates.js.map +1 -1
  544. package/dist/web/themes.d.ts +205 -0
  545. package/dist/web/themes.d.ts.map +1 -0
  546. package/dist/web/themes.js +277 -0
  547. package/dist/web/themes.js.map +1 -0
  548. package/dist/webmention/discovery.d.ts +51 -0
  549. package/dist/webmention/discovery.d.ts.map +1 -0
  550. package/dist/webmention/discovery.js +176 -0
  551. package/dist/webmention/discovery.js.map +1 -0
  552. package/dist/webmention/html.d.ts +71 -0
  553. package/dist/webmention/html.d.ts.map +1 -0
  554. package/dist/webmention/html.js +285 -0
  555. package/dist/webmention/html.js.map +1 -0
  556. package/dist/webmention/index.d.ts +12 -0
  557. package/dist/webmention/index.d.ts.map +1 -0
  558. package/dist/webmention/index.js +8 -0
  559. package/dist/webmention/index.js.map +1 -0
  560. package/dist/webmention/links.d.ts +18 -0
  561. package/dist/webmention/links.d.ts.map +1 -0
  562. package/dist/webmention/links.js +64 -0
  563. package/dist/webmention/links.js.map +1 -0
  564. package/dist/webmention/microformats.d.ts +93 -0
  565. package/dist/webmention/microformats.d.ts.map +1 -0
  566. package/dist/webmention/microformats.js +376 -0
  567. package/dist/webmention/microformats.js.map +1 -0
  568. package/dist/webmention/receive.d.ts +141 -0
  569. package/dist/webmention/receive.d.ts.map +1 -0
  570. package/dist/webmention/receive.js +264 -0
  571. package/dist/webmention/receive.js.map +1 -0
  572. package/dist/webmention/routes.d.ts +37 -0
  573. package/dist/webmention/routes.d.ts.map +1 -0
  574. package/dist/webmention/routes.js +75 -0
  575. package/dist/webmention/routes.js.map +1 -0
  576. package/dist/webmention/service.d.ts +94 -0
  577. package/dist/webmention/service.d.ts.map +1 -0
  578. package/dist/webmention/service.js +207 -0
  579. package/dist/webmention/service.js.map +1 -0
  580. package/package.json +11 -6
  581. package/templates/site/content/_data/site.json +1 -0
  582. package/templates/site/content/posts/2026-01-01-hello-world.md +5 -3
  583. package/templates/site/geekity.config.ts +11 -1
  584. package/templates/site/tsconfig.json +1 -1
  585. package/themes/default/README.md +1096 -50
  586. package/themes/default/layouts/404.njk +2 -3
  587. package/themes/default/layouts/author.njk +37 -0
  588. package/themes/default/layouts/base.njk +172 -31
  589. package/themes/default/layouts/category.njk +27 -0
  590. package/themes/default/layouts/front-page.njk +57 -0
  591. package/themes/default/layouts/home.njk +17 -1
  592. package/themes/default/layouts/page.njk +57 -5
  593. package/themes/default/layouts/post.njk +78 -19
  594. package/themes/default/layouts/search.njk +57 -0
  595. package/themes/default/layouts/tag.njk +3 -6
  596. package/themes/default/mail/comment-digest.html.njk +37 -0
  597. package/themes/default/mail/comment-digest.subject.njk +3 -0
  598. package/themes/default/mail/comment-digest.txt.njk +40 -0
  599. package/themes/default/mail/comment-pending.html.njk +27 -0
  600. package/themes/default/mail/comment-pending.subject.njk +2 -0
  601. package/themes/default/mail/comment-pending.txt.njk +24 -0
  602. package/themes/default/mail/comment-reply.html.njk +21 -0
  603. package/themes/default/mail/comment-reply.subject.njk +2 -0
  604. package/themes/default/mail/comment-reply.txt.njk +22 -0
  605. package/themes/default/mail/contact-message.html.njk +24 -0
  606. package/themes/default/mail/contact-message.subject.njk +5 -0
  607. package/themes/default/mail/contact-message.txt.njk +20 -0
  608. package/themes/default/mail/password-changed.html.njk +18 -0
  609. package/themes/default/mail/password-changed.subject.njk +2 -0
  610. package/themes/default/mail/password-changed.txt.njk +17 -0
  611. package/themes/default/mail/password-reset.html.njk +22 -0
  612. package/themes/default/mail/password-reset.subject.njk +2 -0
  613. package/themes/default/mail/password-reset.txt.njk +18 -0
  614. package/themes/default/mail/test.html.njk +14 -0
  615. package/themes/default/mail/test.subject.njk +2 -0
  616. package/themes/default/mail/test.txt.njk +12 -0
  617. package/themes/default/partials/archive.njk +27 -0
  618. package/themes/default/partials/bio.njk +77 -0
  619. package/themes/default/partials/comment-form.njk +109 -0
  620. package/themes/default/partials/contact-form.njk +88 -0
  621. package/themes/default/partials/conversation.njk +139 -0
  622. package/themes/default/partials/feeds.njk +26 -0
  623. package/themes/default/partials/jsonld.njk +120 -0
  624. package/themes/default/partials/pagination.njk +12 -6
  625. package/themes/default/partials/post-list.njk +51 -18
  626. package/themes/default/partials/search-form.njk +17 -0
  627. package/themes/default/partials/tags.njk +26 -5
  628. package/themes/default/static/highlight.js +4 -0
  629. package/themes/default/static/style.css +1119 -194
  630. package/themes/default/theme.json +5 -0
  631. package/admin/layouts/document-editor.njk +0 -167
  632. package/admin/layouts/federation.njk +0 -145
  633. package/admin/layouts/login.njk +0 -23
  634. package/admin/layouts/settings.njk +0 -116
  635. package/admin/layouts/setup.njk +0 -27
  636. package/admin/layouts/users.njk +0 -91
  637. /package/admin/{layouts/_flash.njk → components/flash.njk} +0 -0
@@ -0,0 +1,487 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { readdirSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { COMMENT_KINDS, COMMENT_SOURCES, COMMENT_STATUSES } from '../admin/store.js';
5
+ import { readFileIfPresentSync, withFileLock, writeFileAtomicallySync } from '../files/atomic.js';
6
+ import { hashClientAddress } from '../forms/protection.js';
7
+ /**
8
+ * Native comments as the files that hold them, and the one door into them.
9
+ *
10
+ * decision-9 makes the filesystem the source of truth for everything durable,
11
+ * and a comment somebody typed into this site is exactly that: nothing else
12
+ * holds it, and no other server can be asked for it again. So it lives under
13
+ * `content/_data/comments/`, one JSON file per post, which puts it in git
14
+ * beside the posts, hands it to an Eleventy build of the same directory, and
15
+ * leaves SQLite holding nothing but an index — one rebuilt from these files on
16
+ * every boot and deletable at rest.
17
+ *
18
+ * The two rules `federation/records.ts` follows apply here for the same
19
+ * reasons, and are why every writer in this module goes through
20
+ * {@link withFileLock}:
21
+ *
22
+ * - **The file first, the index inside the same step.** Two comments arriving
23
+ * at once cannot leave the index saying something the file does not.
24
+ * - **The index is derived, never told.** A row is built from a file entry by
25
+ * {@link commentFrom} whether it is being written for the first time or
26
+ * rebuilt at boot.
27
+ *
28
+ * The entry shape is deliberately wider than a form submission. A webmention
29
+ * (TASK-51) is somebody else's post pointing at this one: it has a `url` and no
30
+ * email, it may be a like rather than a reply, and it belongs in the same file
31
+ * and the same thread. So every entry says its `source` and its `kind`, and
32
+ * nothing here assumes a person filled in a form.
33
+ *
34
+ * Three things write comments — the form under a post, the webmention endpoint
35
+ * and a moderator's reply on the admin screen — and they all go through
36
+ * {@link intakeComment}, which is the whole of what happens between a proposed
37
+ * comment and a comment existing: the address hashed, the approved-author rule,
38
+ * the {@link CommentChecker}, one verdict-to-status rule, the write, and the
39
+ * message to whoever was waiting to hear. What the callers keep is what is
40
+ * really theirs — parsing a form and its cheap defences, fetching and verifying
41
+ * a source, and knowing who is signed in.
42
+ */
43
+ /** Where the comment files live, relative to the content directory. */
44
+ export const COMMENTS_DATA_DIRECTORY = '_data/comments';
45
+ /** The absolute path of one site's `content/_data/comments`. */
46
+ export function commentsDirectory(contentDir) {
47
+ return path.join(contentDir, ...COMMENTS_DATA_DIRECTORY.split('/'));
48
+ }
49
+ /**
50
+ * The absolute path of one post's comment file.
51
+ *
52
+ * Named by the post's slug, which is what a post keeps for its whole life: the
53
+ * editor gives an existing document the slug it already has, so the file that
54
+ * holds a post's comments never has to move. A slug carrying anything a
55
+ * filename should not is refused rather than sanitised, because two slugs that
56
+ * sanitised to one name would silently share a thread.
57
+ */
58
+ export function commentsFile(contentDir, slug) {
59
+ if (!SAFE_SLUG.test(slug)) {
60
+ throw new Error(`"${slug}" is not a slug a comment file can be named after.`);
61
+ }
62
+ return path.join(commentsDirectory(contentDir), `${slug}.json`);
63
+ }
64
+ /** Slugs a file may be named after: what {@link slugify} produces, and no more. */
65
+ const SAFE_SLUG = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
66
+ /**
67
+ * One post's comments, as its file says them, oldest first.
68
+ *
69
+ * A missing file is no comments, which is what almost every post has. Unlike
70
+ * `followers.json`, an entry that will not parse is dropped rather than
71
+ * throwing: a comment file is public, in git and editable by hand, and a typo
72
+ * in one post's file should cost that entry rather than take the whole site
73
+ * down on the next boot. Nothing irreplaceable is at stake — a comment that
74
+ * cannot be read is one nobody could have shown either way.
75
+ */
76
+ export function readComments(contentDir, slug) {
77
+ const source = readFileIfPresentSync(commentsFile(contentDir, slug));
78
+ if (source === undefined)
79
+ return [];
80
+ return commentsIn(source);
81
+ }
82
+ /** One post's comment file as the post it names and the comments it holds. */
83
+ function fileContents(source) {
84
+ let parsed;
85
+ try {
86
+ parsed = JSON.parse(source);
87
+ }
88
+ catch {
89
+ return { post: '', comments: [] };
90
+ }
91
+ if (!isRecord(parsed))
92
+ return { post: '', comments: [] };
93
+ const entries = parsed['comments'];
94
+ return {
95
+ post: typeof parsed['post'] === 'string' ? parsed['post'] : '',
96
+ comments: Array.isArray(entries)
97
+ ? entries.map(commentFrom).filter((entry) => entry !== undefined)
98
+ : [],
99
+ };
100
+ }
101
+ /** Just the comments, for the readers that have the post already. */
102
+ function commentsIn(source) {
103
+ return fileContents(source).comments;
104
+ }
105
+ /**
106
+ * One file entry as the record it is, or `undefined` when it is not one.
107
+ *
108
+ * An entry needs an id and a name and nothing else: everything a hand-written
109
+ * file leaves out gets the value that means "the site was not told", so a
110
+ * template printing it has something to print. An entry with no id is refused,
111
+ * because an id is what a reply names and what a moderation action moves.
112
+ */
113
+ function commentFrom(value) {
114
+ if (!isRecord(value))
115
+ return undefined;
116
+ const id = value['id'];
117
+ if (typeof id !== 'string' || id === '')
118
+ return undefined;
119
+ const author = isRecord(value['author']) ? value['author'] : {};
120
+ const content = isRecord(value['content']) ? value['content'] : {};
121
+ const name = typeof author['name'] === 'string' ? author['name'] : '';
122
+ if (name === '')
123
+ return undefined;
124
+ return {
125
+ id,
126
+ source: oneOf(value['source'], COMMENT_SOURCES, 'comment'),
127
+ kind: oneOf(value['kind'], COMMENT_KINDS, 'reply'),
128
+ // An entry that does not say where it stands is waiting: showing something
129
+ // nobody approved would be the one mistake this cannot make.
130
+ status: oneOf(value['status'], COMMENT_STATUSES, 'pending'),
131
+ author: {
132
+ name,
133
+ url: optionalText(author['url']),
134
+ email: optionalText(author['email']),
135
+ avatar: optionalText(author['avatar']),
136
+ },
137
+ content: {
138
+ markdown: typeof content['markdown'] === 'string' ? content['markdown'] : '',
139
+ html: typeof content['html'] === 'string' ? content['html'] : '',
140
+ },
141
+ submitted: typeof value['submitted'] === 'string' ? value['submitted'] : '',
142
+ addressHash: optionalText(value['addressHash']),
143
+ inReplyTo: optionalText(value['inReplyTo']),
144
+ url: optionalText(value['url']),
145
+ // An entry that does not say means one that never asked: a comment file
146
+ // written before TASK-55 shipped, or edited by hand, has nobody waiting on
147
+ // it, and defaulting the other way would email people who never opted in.
148
+ notify: value['notify'] === true,
149
+ };
150
+ }
151
+ /**
152
+ * Record a comment: append it to its post's file and index it.
153
+ *
154
+ * The id is minted here unless the caller brought one, and it is a UUID rather
155
+ * than a counter so two comments written at once on two machines holding the
156
+ * same content directory can never be given the same name.
157
+ */
158
+ export async function addComment(records, comment) {
159
+ const { admin, contentDir } = records;
160
+ const file = commentsFile(contentDir, comment.slug);
161
+ const stored = {
162
+ ...commentRecordOf(comment),
163
+ slug: comment.slug,
164
+ permalink: comment.permalink,
165
+ };
166
+ return await withFileLock(file, () => {
167
+ const held = readCommentFile(file);
168
+ writeFileAtomicallySync(file, commentsJson(comment.permalink, [...held, entryOf(stored)]));
169
+ admin.putComment(stored);
170
+ return stored;
171
+ });
172
+ }
173
+ /**
174
+ * The one door into `content/_data/comments/`.
175
+ *
176
+ * Everything between a proposed comment and a comment existing happens here,
177
+ * so the form, the webmention endpoint and the admin screen cannot drift into
178
+ * three different answers to the same four questions.
179
+ *
180
+ * **Where the site would put it, before anybody judged it.** A form comment
181
+ * whose name and email together have been approved before is approved again —
182
+ * WordPress's rule, and the whole of the auto-approval decision. A webmention
183
+ * waits, because a page linking here is as much a stranger's words as a form
184
+ * submission is. A moderator's reply is approved, because the person writing it
185
+ * is the person who would have approved it.
186
+ *
187
+ * **What the checker says.** Everything but a moderator's own words goes
188
+ * through {@link CommentChecker}; a checker that is down or throws is no
189
+ * opinion, so a service having a bad afternoon never stops a site taking
190
+ * comments. A moderator's reply is not offered to it at all: a spam service has
191
+ * no say in what the owner of the site says.
192
+ *
193
+ * **The one verdict-to-status rule.**
194
+ *
195
+ * | Verdict | A new comment | A webmention sent again |
196
+ * | --------- | ------------------------------------ | ------------------------------------ |
197
+ * | `discard` | Nothing is stored. | The held entry is deleted. |
198
+ * | `spam` | Filed as spam. | Filed as spam. |
199
+ * | `ham` | Approved. | The moderator's decision stands. |
200
+ * | `unknown` | Where the site would have put it. | The moderator's decision stands. |
201
+ *
202
+ * A source re-sending its webmention must not take an approved mention back
203
+ * into the queue, and must not quietly let a spam one out. A checker that has
204
+ * changed its mind to `spam` is the one thing that moves it, because that is a
205
+ * new fact about the content rather than a repeat of an old one.
206
+ *
207
+ * **Who hears about it.** A new entry that is waiting sends the moderation
208
+ * notice, once. A webmention that was merely rewritten sends nothing — the
209
+ * moderators heard the first time and it has been in the queue ever since —
210
+ * and neither does one that was approved or filed as spam, because neither is
211
+ * waiting for anybody. A new comment that is approved instead tells whoever it
212
+ * answers, when they asked to be told.
213
+ */
214
+ export async function intakeComment(options) {
215
+ const { records, origin, comment } = options;
216
+ // What this site would do about it on its own, which is both what a checker
217
+ // is shown and what `unknown` falls back to.
218
+ const proposed = {
219
+ ...comment,
220
+ status: siteStatusFor(origin, records, comment),
221
+ addressHash: hashClientAddress(options.dataDir, options.address),
222
+ };
223
+ const held = origin === 'webmention' ? heldWebmention(records, comment.slug, comment.url) : undefined;
224
+ const verdict = origin === 'moderator' ? 'unknown' : await ask(options, proposed);
225
+ if (verdict === 'discard') {
226
+ if (held === undefined)
227
+ return { kind: 'discarded', removed: false };
228
+ await deleteComment(records, held.id);
229
+ return { kind: 'discarded', removed: true };
230
+ }
231
+ const status = statusFor(verdict, proposed.status, held);
232
+ if (held !== undefined) {
233
+ // Its id, its post and its source never move: that is what makes it the
234
+ // same comment. What the page now says about itself replaces what it said.
235
+ const moved = await updateComment(records, held.id, {
236
+ kind: comment.kind,
237
+ status,
238
+ author: comment.author,
239
+ content: comment.content,
240
+ submitted: comment.submitted,
241
+ });
242
+ return moved === undefined
243
+ ? { kind: 'gone' }
244
+ : { kind: 'stored', comment: moved, created: false };
245
+ }
246
+ const stored = await addComment(records, { ...proposed, status });
247
+ announce(options.notices, stored);
248
+ return { kind: 'stored', comment: stored, created: true };
249
+ }
250
+ /**
251
+ * The webmention this site already holds from that page, if any.
252
+ *
253
+ * The source URL is a webmention's identity (doc-7), so this is what stops a
254
+ * blog post that is edited and re-sent adding a second entry — and what the
255
+ * endpoint asks before it fetches, so a source that has stopped linking here
256
+ * knows what to take away.
257
+ */
258
+ export function heldWebmention(records, slug, source) {
259
+ if (source === null || source === '')
260
+ return undefined;
261
+ return records.admin
262
+ .listCommentsFor(slug)
263
+ .find((comment) => comment.source === 'webmention' && comment.url === source);
264
+ }
265
+ /** Where this site's own rules put a comment, before a checker has spoken. */
266
+ function siteStatusFor(origin, records, comment) {
267
+ if (origin === 'moderator')
268
+ return 'approved';
269
+ if (origin === 'webmention')
270
+ return 'pending';
271
+ return records.admin.hasApprovedAuthor(comment.author.name, comment.author.email)
272
+ ? 'approved'
273
+ : 'pending';
274
+ }
275
+ /** The verdict-to-status rule, in the one place it is written. */
276
+ function statusFor(verdict, site, held) {
277
+ if (verdict === 'spam')
278
+ return 'spam';
279
+ if (held !== undefined)
280
+ return held.status;
281
+ return verdict === 'ham' ? 'approved' : site;
282
+ }
283
+ /** Ask the checker, if there is one, and treat a broken one as no opinion. */
284
+ async function ask(options, comment) {
285
+ const checker = options.checker;
286
+ if (checker === undefined)
287
+ return 'unknown';
288
+ const post = options.post;
289
+ const submission = {
290
+ comment,
291
+ post: {
292
+ slug: comment.slug,
293
+ title: post?.title ?? comment.slug,
294
+ url: post?.url ?? absolute(comment.permalink, options.baseUrl),
295
+ },
296
+ address: options.address,
297
+ userAgent: options.userAgent,
298
+ referrer: options.referrer,
299
+ baseUrl: options.baseUrl,
300
+ };
301
+ try {
302
+ return await checker.check(submission);
303
+ }
304
+ catch (error) {
305
+ // A checker that is down must not stop a site taking comments; the comment
306
+ // falls back to whatever the site's own rules said about it.
307
+ const logger = options.logger ?? console;
308
+ logger.warn(`The comment checker refused to answer: ${messageOf(error)}`);
309
+ return 'unknown';
310
+ }
311
+ }
312
+ /** Tell whoever was waiting on a comment that has just been written. */
313
+ function announce(notices, stored) {
314
+ if (notices === undefined)
315
+ return;
316
+ // Only `pending`: one a checker filed as spam is not waiting for anybody,
317
+ // and one it said to discard was never stored at all.
318
+ if (stored.status === 'pending')
319
+ notices.pending(stored);
320
+ // A comment the site let straight through is on the page already, so
321
+ // whoever it answers hears about it now rather than at a moderator's hand.
322
+ else if (stored.status === 'approved')
323
+ notices.replyApproved(stored);
324
+ }
325
+ /** A site-root path as an absolute URL. */
326
+ function absolute(pathname, baseUrl) {
327
+ try {
328
+ return new URL(pathname, baseUrl).href;
329
+ }
330
+ catch {
331
+ return pathname;
332
+ }
333
+ }
334
+ function messageOf(error) {
335
+ return error instanceof Error ? error.message : String(error);
336
+ }
337
+ /**
338
+ * Move a stored comment: approve it, mark it spam, put it back, or rewrite
339
+ * what it says.
340
+ *
341
+ * The rewrite is what a webmention needs (TASK-51): a page that links here and
342
+ * is then edited sends its webmention again, and what this site shows should
343
+ * be what that page says now rather than what it said last month. Its id, the
344
+ * post it is on and the source it came from never move, which is what makes it
345
+ * the same comment.
346
+ *
347
+ * Returns the comment as it now stands, or `undefined` when the index has no
348
+ * such comment. The index is what says which post's file to open, which is the
349
+ * one thing an id alone does not carry.
350
+ */
351
+ export async function updateComment(records, id, change) {
352
+ const { admin, contentDir } = records;
353
+ const known = admin.getComment(id);
354
+ if (known === undefined)
355
+ return undefined;
356
+ const file = commentsFile(contentDir, known.slug);
357
+ return await withFileLock(file, () => {
358
+ const held = readCommentFile(file);
359
+ const at = held.findIndex((entry) => entry.id === id);
360
+ if (at === -1)
361
+ return undefined;
362
+ const moved = { ...known, ...held[at], ...change };
363
+ writeFileAtomicallySync(file, commentsJson(known.permalink, held.with(at, entryOf(moved))));
364
+ admin.putComment(moved);
365
+ return moved;
366
+ });
367
+ }
368
+ /**
369
+ * Forget a comment altogether.
370
+ *
371
+ * Returns whether there was anything to forget. The index is emptied of the id
372
+ * whether or not the file held it, so a row left over from a file somebody
373
+ * edited by hand cannot go on being shown.
374
+ */
375
+ export async function deleteComment(records, id) {
376
+ const { admin, contentDir } = records;
377
+ const known = admin.getComment(id);
378
+ if (known === undefined)
379
+ return false;
380
+ const file = commentsFile(contentDir, known.slug);
381
+ return await withFileLock(file, () => {
382
+ const held = readCommentFile(file);
383
+ const next = held.filter((entry) => entry.id !== id);
384
+ if (next.length !== held.length) {
385
+ writeFileAtomicallySync(file, commentsJson(known.permalink, next));
386
+ }
387
+ const indexed = admin.deleteComment(id);
388
+ return next.length !== held.length || indexed;
389
+ });
390
+ }
391
+ /**
392
+ * Rebuild the comment index from the files.
393
+ *
394
+ * This is what makes `data/geekity.db` disposable for comments (decision-9):
395
+ * the table holds nothing but what these files say, so every boot throws away
396
+ * what it holds and reads them again. Editing a comment file by hand and
397
+ * restarting is therefore a supported thing to do, and a database deleted at
398
+ * rest costs a site nothing.
399
+ *
400
+ * Exported so `geekity rebuild` can call the very function boot calls, and
401
+ * synchronous because both callers are.
402
+ */
403
+ export function rebuildCommentIndexes(records) {
404
+ const { admin, contentDir } = records;
405
+ const comments = [];
406
+ for (const file of commentFiles(contentDir)) {
407
+ const source = readFileIfPresentSync(file);
408
+ if (source === undefined)
409
+ continue;
410
+ const slug = path.basename(file, '.json');
411
+ const held = fileContents(source);
412
+ for (const entry of held.comments) {
413
+ comments.push({ ...entry, slug, permalink: held.post });
414
+ }
415
+ }
416
+ admin.replaceComments(comments);
417
+ return { comments: comments.length };
418
+ }
419
+ /** Every comment file a site has, in a stable order. */
420
+ function commentFiles(contentDir) {
421
+ let entries;
422
+ try {
423
+ entries = readdirSync(commentsDirectory(contentDir));
424
+ }
425
+ catch {
426
+ // No directory is no comments, which is what a site nobody has answered
427
+ // has. Every other way this can fail fails again on the next boot.
428
+ return [];
429
+ }
430
+ return entries
431
+ .filter((name) => name.endsWith('.json') && SAFE_SLUG.test(name.slice(0, -'.json'.length)))
432
+ .sort()
433
+ .map((name) => path.join(commentsDirectory(contentDir), name));
434
+ }
435
+ /** One file's comments, read inside a lock the caller already holds. */
436
+ function readCommentFile(file) {
437
+ const source = readFileIfPresentSync(file);
438
+ return source === undefined ? [] : commentsIn(source);
439
+ }
440
+ /** A new comment as the record it becomes, id and all. */
441
+ function commentRecordOf(comment) {
442
+ return {
443
+ id: comment.id ?? randomUUID(),
444
+ source: comment.source,
445
+ kind: comment.kind,
446
+ status: comment.status,
447
+ author: { ...comment.author },
448
+ content: { ...comment.content },
449
+ submitted: comment.submitted,
450
+ addressHash: comment.addressHash,
451
+ inReplyTo: comment.inReplyTo,
452
+ url: comment.url,
453
+ notify: comment.notify,
454
+ };
455
+ }
456
+ /** A row as the file spells it: the post it is on is the file's, not the entry's. */
457
+ function entryOf(comment) {
458
+ return {
459
+ id: comment.id,
460
+ source: comment.source,
461
+ kind: comment.kind,
462
+ status: comment.status,
463
+ author: { ...comment.author },
464
+ content: { ...comment.content },
465
+ submitted: comment.submitted,
466
+ addressHash: comment.addressHash,
467
+ inReplyTo: comment.inReplyTo,
468
+ url: comment.url,
469
+ notify: comment.notify,
470
+ };
471
+ }
472
+ /** One post's comments as the file spells them: indented, newline ended. */
473
+ function commentsJson(permalink, comments) {
474
+ return `${JSON.stringify({ post: permalink, comments }, null, 2)}\n`;
475
+ }
476
+ /** A value the file may have left out, as the text it holds or `null`. */
477
+ function optionalText(value) {
478
+ return typeof value === 'string' && value !== '' ? value : null;
479
+ }
480
+ /** A stored enumeration value, or the fallback for one this version does not know. */
481
+ function oneOf(value, allowed, fallback) {
482
+ return allowed.includes(value) ? value : fallback;
483
+ }
484
+ function isRecord(value) {
485
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
486
+ }
487
+ //# sourceMappingURL=records.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"records.js","sourceRoot":"","sources":["../../src/comments/records.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import type { Hono } from 'hono';
2
+ import type { AdminStore } from '../admin/store.ts';
3
+ import type { Document } from '../content/document.ts';
4
+ import type { GeekityEnv } from '../env.ts';
5
+ import type { CommentFormContext } from './form.ts';
6
+ /**
7
+ * The one public endpoint comments add: where the form under a post posts to.
8
+ *
9
+ * It is a single path rather than one per post because the post is a field of
10
+ * the form. That keeps it out of the way of every permalink a site might ever
11
+ * mint — a path under `/_geekity/` is the CMS's own, like the health check —
12
+ * and means the route table does not grow with the site.
13
+ *
14
+ * Everything it answers is either a redirect or the post's own page rendered
15
+ * again with the form still filled in. There is no JSON, no fragment and no
16
+ * script: a comment form has worked without JavaScript since 2003 and there is
17
+ * no reason for this one to stop.
18
+ */
19
+ export { COMMENT_POST_PATH, COMMENT_REPLY_PARAM } from './form.ts';
20
+ /** Register the comment endpoint. Mounted by the public site. */
21
+ export declare function mountComments(app: Hono<GeekityEnv>): void;
22
+ /**
23
+ * The thank-you a reader is shown after posting, from the query the redirect
24
+ * carried, or `undefined` when the URL says nothing.
25
+ *
26
+ * A query rather than a flash message, because a reader has no session to hang
27
+ * one on, and because a comment form that only works for people with cookies
28
+ * is not a comment form.
29
+ */
30
+ export declare function commentNoticeFor(query: string | undefined): string | undefined;
31
+ /**
32
+ * The comment a `?reply_to=` query points at, as the theme needs it, or an
33
+ * empty object when the query names nothing on this post.
34
+ *
35
+ * Checked against the index rather than trusted, so a made-up id — or one from
36
+ * another page — cannot put a stranger's name on somebody's reply form, and so
37
+ * a comment waiting for a moderator cannot be discovered by guessing.
38
+ */
39
+ export declare function commentReplyTarget(options: {
40
+ admin: AdminStore;
41
+ document: Document;
42
+ id: string | undefined;
43
+ }): Record<string, unknown>;
44
+ /**
45
+ * The form for a post, or `undefined` when it is not taking comments.
46
+ *
47
+ * What the renderer is handed at boot. It reads the settings and the clock on
48
+ * every render, so a post that closed an hour ago stops offering a form on the
49
+ * very next request.
50
+ */
51
+ export declare function commentFormFor(options: {
52
+ document: Document;
53
+ site: Record<string, unknown>;
54
+ now: Date;
55
+ /** Whether the site can send mail, which is whether the box is offered. */
56
+ notifiable?: boolean | undefined;
57
+ }): CommentFormContext | undefined;
58
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/comments/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,MAAM,CAAC;AAE1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAU5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAWpD;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEnE,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,CAiGzD;AAqBD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAE9E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IACnB,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;CACxB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQ1B;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE;IACtC,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,GAAG,EAAE,IAAI,CAAC;IACV,2EAA2E;IAC3E,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAClC,GAAG,kBAAkB,GAAG,SAAS,CAGjC"}