@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
@@ -3,26 +3,6 @@ export interface OpenAdminStoreOptions {
3
3
  /** Directory the database lives in. Created if it is missing. */
4
4
  dataDir: string;
5
5
  }
6
- /** An admin user. There is one role, so there is no role column. */
7
- export interface User {
8
- /** Row id, stable for the life of the user. */
9
- readonly id: number;
10
- /** Login name, unique and compared case sensitively. */
11
- readonly username: string;
12
- /** When the user was created, as an ISO 8601 instant. */
13
- readonly createdAt: string;
14
- }
15
- /** A user with the column no screen may render: its password hash. */
16
- export interface StoredUser extends User {
17
- /** The PHC-encoded argon2id hash. */
18
- readonly passwordHash: string;
19
- }
20
- /** What {@link AdminStore.createUser} is given. */
21
- export interface CreateUserInput {
22
- username: string;
23
- /** The plain password. It is hashed on the way in and never stored. */
24
- password: string;
25
- }
26
6
  /**
27
7
  * A login, or the anonymous session that carries a CSRF token to a visitor who
28
8
  * has not logged in yet.
@@ -70,45 +50,59 @@ export interface CreateSessionInput {
70
50
  /** How long a session id is, in bytes. 32 is the 256 bits doc-5 asks for. */
71
51
  export declare const SESSION_ID_BYTES = 32;
72
52
  /**
73
- * The two signature algorithms doc-4 asks a site actor to hold.
53
+ * One outstanding password reset: the right to set one user's password once,
54
+ * until it is used or it expires.
74
55
  *
75
- * `RSASSA-PKCS1-v1_5` signs HTTP Signatures, which is what Mastodon and most
76
- * of the fediverse verify; `Ed25519` signs FEP-8b32 object integrity proofs.
77
- * They are Fedify's own spellings, so a row round-trips into
78
- * `generateCryptoKeyPair` without translation.
56
+ * The token is only ever in hand at the moment it is made, because the link is
57
+ * the only copy: the database keeps its SHA-256, so reading the table tells
58
+ * somebody that a reset was asked for and nothing more.
79
59
  */
80
- export declare const ACTOR_KEY_ALGORITHMS: readonly ['RSASSA-PKCS1-v1_5', 'Ed25519'];
81
- /** One of {@link ACTOR_KEY_ALGORITHMS}. */
82
- export type ActorKeyAlgorithm = (typeof ACTOR_KEY_ALGORITHMS)[number];
83
- /** A stored actor key pair, both halves as serialized JWK. */
84
- export interface ActorKey {
85
- /**
86
- * Which actor the pair belongs to. The site actor's identifier is a
87
- * constant, not the handle, so renaming the handle does not orphan the keys.
88
- */
89
- readonly identifier: string;
90
- /** Which algorithm the pair is for. */
91
- readonly algorithm: ActorKeyAlgorithm;
92
- /** The private key as JWK, JSON encoded. Never leaves the server. */
93
- readonly privateJwk: string;
94
- /** The public key as JWK, JSON encoded. This is what the actor publishes. */
95
- readonly publicJwk: string;
96
- /** When the pair was generated, as an ISO 8601 instant. */
60
+ export interface PasswordReset {
61
+ /** Whose password this sets. */
62
+ readonly userId: number;
63
+ /** When it was asked for, as an ISO 8601 instant. */
97
64
  readonly createdAt: string;
65
+ /** When it stops working, as an ISO 8601 instant. */
66
+ readonly expiresAt: string;
67
+ }
68
+ /** A {@link PasswordReset} with the one thing the database never keeps. */
69
+ export interface IssuedPasswordReset extends PasswordReset {
70
+ /**
71
+ * The token itself, 256 random bits hex encoded, handed back exactly once.
72
+ * It goes in the link and nowhere else.
73
+ */
74
+ readonly token: string;
75
+ }
76
+ /** What {@link AdminStore.createPasswordReset} is given. */
77
+ export interface CreatePasswordResetInput {
78
+ /** Whose password the link will set. */
79
+ userId: number;
80
+ /** How long the link lasts, in seconds. */
81
+ lifetimeSeconds: number;
82
+ /** The clock, injectable so expiry is testable. Defaults to now. */
83
+ now?: Date | undefined;
98
84
  }
99
- /** An {@link ActorKey} before the store has stamped it with a creation time. */
100
- export type NewActorKey = Omit<ActorKey, 'createdAt'>;
101
85
  /**
102
86
  * A remote actor that follows this site.
103
87
  *
104
- * doc-4 keeps the followers in SQLite rather than in Fedify's KV store,
105
- * because they are the one thing an ActivityPub site cannot regenerate: an
106
- * actor that followed and was forgotten never hears from the site again and
107
- * has no way of noticing. The display columns are a copy of what the actor
108
- * said about itself when it followed, so the admin can list its followers
109
- * without dereferencing every one of them.
88
+ * The followers are irreplaceable — an actor that followed and was forgotten
89
+ * never hears from the site again and has no way of noticing — so
90
+ * `content/_data/federation/followers.json` is where they are kept
91
+ * (decision-9), and this row is the index of that file, emptied and read back
92
+ * on every boot. The display columns are a copy of what the actor said about
93
+ * itself when it followed, so the admin can list its followers without
94
+ * dereferencing every one of them.
110
95
  */
111
96
  export interface Follower {
97
+ /**
98
+ * Which user is followed (decision-14).
99
+ *
100
+ * Every user is an actor with followers of their own, so a follower is only
101
+ * ever a follower *of somebody*: the column is what makes one person's
102
+ * followers collection, and one person's delivery, theirs. It is the name of
103
+ * the directory the file came from rather than anything the file says.
104
+ */
105
+ readonly username: string;
112
106
  /** The follower's ActivityStreams id, which is what identifies it. */
113
107
  readonly actorId: string;
114
108
  /** Where an activity addressed to this follower is delivered. */
@@ -166,40 +160,48 @@ export interface InboxActivity {
166
160
  readonly actorId: string;
167
161
  /** What it was about — the post that was liked, the note replied to — or `null`. */
168
162
  readonly objectId: string | null;
163
+ /**
164
+ * Which of this site's users it was addressed to, or `null` for one
165
+ * delivered to the shared inbox without naming one.
166
+ *
167
+ * decision-14 makes every user an actor, so the log records whose inbox was
168
+ * told as well as what it was told. Derived from the log line rather than
169
+ * from the activity, because the addressee is a fact about the delivery.
170
+ */
171
+ readonly recipient: string | null;
172
+ /**
173
+ * The object this activity answers, for a `Create` that carried an
174
+ * `inReplyTo`, and `null` for everything else.
175
+ *
176
+ * Derived from {@link InboxActivity.json} by {@link replyTargetOf} rather
177
+ * than supplied, so an index rebuilt from the inbox log holds exactly what
178
+ * the live one holds. It is a column so a post's replies can be counted
179
+ * without reading every activity the site was ever sent.
180
+ */
181
+ readonly inReplyTo: string | null;
169
182
  /** When it arrived, as an ISO 8601 instant. */
170
183
  readonly receivedAt: string;
171
184
  /** The activity as compacted JSON-LD, exactly as it was received. */
172
185
  readonly json: string;
173
186
  }
174
- /** An {@link InboxActivity} before the store has given it a row and a time. */
175
- export type NewInboxActivity = Omit<InboxActivity, 'id' | 'receivedAt'>;
176
187
  /**
177
- * One activity this site sent, kept whole so it can be sent again.
188
+ * An {@link InboxActivity} before the store has given it a row and the reply
189
+ * target it derives.
178
190
  *
179
- * decision-5 accepts that an activity queued when the process exits is lost,
180
- * on the understanding that the outcome is recorded per follower and that an
181
- * admin can re-send. Re-sending needs the activity itself, and rebuilding one
182
- * from the content directory is not always possible — a `Delete` is about a
183
- * post that is no longer there — so the compacted JSON-LD is stored as it went
184
- * out.
191
+ * `receivedAt` is optional and defaults to now, exactly as a follower's
192
+ * `followedAt` is: naming one is what lets the boot rebuild put the log's own
193
+ * arrival times back rather than stamping every activity with the moment the
194
+ * index was rebuilt.
185
195
  */
186
- export interface OutboundActivity {
187
- /** The activity's own id, which is what a redelivery is asked for by. */
188
- readonly activityId: string;
189
- /** `Create`, `Update` or `Delete`. */
190
- readonly activityType: string;
191
- /** The ActivityStreams id of the post the activity is about. */
192
- readonly objectId: string;
193
- /** The post's slug when the activity was built, for a human reading the log. */
194
- readonly slug: string | null;
195
- /** When the activity was first built, as an ISO 8601 instant. */
196
- readonly createdAt: string;
197
- /** The activity as compacted JSON-LD, exactly as it was delivered. */
198
- readonly json: string;
199
- }
200
- /** An {@link OutboundActivity} before the store has timed it. */
201
- export type NewOutboundActivity = Omit<OutboundActivity, 'createdAt'> & {
202
- createdAt?: string | undefined;
196
+ export type NewInboxActivity = Omit<InboxActivity, 'id' | 'receivedAt' | 'inReplyTo' | 'recipient'> & {
197
+ receivedAt?: string | undefined;
198
+ /**
199
+ * Who it was addressed to, when the delivery said. Optional and `null` by
200
+ * default: an activity that reached the shared inbox without naming one of
201
+ * this site's actors is addressed to nobody in particular, and so is every
202
+ * line of a log written before decision-14.
203
+ */
204
+ recipient?: string | null | undefined;
203
205
  };
204
206
  /** How one delivery of one activity to one follower ended. */
205
207
  export declare const DELIVERY_STATUSES: readonly ['sent', 'queued', 'failed'];
@@ -212,11 +214,37 @@ export declare const DELIVERY_STATUSES: readonly ['sent', 'queued', 'failed'];
212
214
  * `failed` is a delivery that threw, with the reason in the row.
213
215
  */
214
216
  export type DeliveryStatus = (typeof DELIVERY_STATUSES)[number];
215
- /** How one attempt to deliver one activity to one follower ended. */
217
+ /**
218
+ * How one attempt to deliver one activity to one recipient ended, and what the
219
+ * activity was.
220
+ *
221
+ * The whole of what SQLite keeps about anything the site has sent (decision-9).
222
+ * No payload goes with it: an activity is rebuilt from the post's file at the
223
+ * moment somebody asks for it to go again, so storing the bytes that went out
224
+ * last time would only be a second, staler answer to the same question. What
225
+ * is left is a cache of outcomes, and a site that deletes its database loses
226
+ * nothing but the record of how yesterday's delivery went.
227
+ *
228
+ * The activity's own columns are here rather than in a table of their own
229
+ * because there is no longer anything else to put in one: they are what the
230
+ * federation screen reads to say what went out and when.
231
+ */
216
232
  export interface Delivery {
217
- /** Which activity was delivered. */
233
+ /** The id of the activity that was delivered. */
218
234
  readonly activityId: string;
219
- /** Which follower it was delivered to. */
235
+ /** `Create`, `Update` or `Delete`. */
236
+ readonly activityType: string;
237
+ /**
238
+ * The ActivityStreams id of what it was about: a post, or the site's own
239
+ * actor when the profile itself was what moved.
240
+ */
241
+ readonly objectId: string;
242
+ /**
243
+ * The post's slug when the activity went out, or `null` for an activity
244
+ * about no post — which is what an actor `Update` is.
245
+ */
246
+ readonly slug: string | null;
247
+ /** Which follower or relay it was delivered to. */
220
248
  readonly actorId: string;
221
249
  /** The inbox actually used, which is the shared one when the follower has one. */
222
250
  readonly inboxId: string;
@@ -231,48 +259,237 @@ export interface Delivery {
231
259
  export type NewDelivery = Omit<Delivery, 'attemptedAt'> & {
232
260
  attemptedAt?: string | undefined;
233
261
  };
262
+ /** Where a relay subscription stands (FEP-ae0c). */
263
+ export declare const RELAY_STATES: readonly ['pending', 'accepted', 'rejected'];
234
264
  /**
235
- * The auth half of the SQLite database: the data doc-1 says lives only there.
265
+ * One of {@link RELAY_STATES}.
236
266
  *
237
- * It is deliberately not part of the {@link ContentStore}. That store is the
238
- * derived index over the Markdown files and can be deleted and rebuilt at any
239
- * time; users and sessions are the one thing in the database that cannot. They
240
- * share the file (one database per site, one set of migrations on boot) and
241
- * nothing else.
267
+ * `pending` is a `Follow` that has gone out and not been answered — which may
268
+ * take days, because a relay is allowed to hold one for a human to approve.
269
+ * `accepted` is one the relay answered `Accept` to, and the only state the
270
+ * site delivers to. `rejected` is one it answered `Reject` to, with whatever
271
+ * it said in {@link Relay.reason}.
242
272
  */
243
- export interface AdminStore {
244
- /** Absolute path of the SQLite file, the same one the content index uses. */
245
- readonly file: string;
246
- /** How many users exist. Zero is what puts the admin into first-run setup. */
247
- countUsers(): number;
248
- /** Every user, by name, without password hashes. */
249
- listUsers(): User[];
250
- /** One user with its password hash, or `undefined`. For auth, not for screens. */
251
- getUser(username: string): StoredUser | undefined;
252
- /** The user behind a session's `userId`, or `undefined`. */
253
- getUserById(id: number): User | undefined;
254
- /**
255
- * Hash the password and insert the user.
273
+ export type RelayState = (typeof RELAY_STATES)[number];
274
+ /**
275
+ * The site's subscription to one relay.
276
+ *
277
+ * The list of relays itself is a setting, and so lives in `site.json`; this is
278
+ * the state of the handshake with each of them, which is operational rather
279
+ * than editorial. Keyed by the inbox because that is the only thing known when the
280
+ * `Follow` goes out: a relay's actor id is not learned until it answers.
281
+ */
282
+ export interface Relay {
283
+ /** The relay's inbox, which is what the site was given and what it delivers to. */
284
+ readonly inboxId: string;
285
+ /** The relay actor's id, once it has answered, and `null` until then. */
286
+ readonly actorId: string | null;
287
+ /** Where the subscription stands. */
288
+ readonly state: RelayState;
289
+ /** Why a `Reject` was a reject, when it gave a reason, or `null`. */
290
+ readonly reason: string | null;
291
+ /** The id of the `Follow` that was sent, which an `Accept` names. */
292
+ readonly followId: string | null;
293
+ /** When the site first subscribed, as an ISO 8601 instant. */
294
+ readonly createdAt: string;
295
+ /** When the subscription last moved — accepted, rejected, re-followed. */
296
+ readonly updatedAt: string;
297
+ }
298
+ /** A {@link Relay} before the store has timed it. */
299
+ export type NewRelay = Omit<Relay, 'createdAt' | 'updatedAt'> & {
300
+ createdAt?: string | undefined;
301
+ };
302
+ /** How one webmention this site sent to one page ended. */
303
+ export declare const WEBMENTION_SEND_STATUSES: readonly ['sent', 'none', 'failed'];
304
+ /**
305
+ * One of {@link WEBMENTION_SEND_STATUSES}.
306
+ *
307
+ * `sent` is an endpoint that accepted the notification. `none` is a page that
308
+ * advertises no endpoint, which is most of the web and is not a failure —
309
+ * recording it is what stops the sender asking the same page again on every
310
+ * edit and what tells a person why nothing went. `failed` is an endpoint that
311
+ * was there and refused, with the reason in the row.
312
+ */
313
+ export type WebmentionSendStatus = (typeof WEBMENTION_SEND_STATUSES)[number];
314
+ /**
315
+ * How one attempt to tell one page that a post links to it ended.
316
+ *
317
+ * The whole of what SQLite keeps about the webmentions a site has sent, and a
318
+ * cache like every other outcome here (decision-9): the links are in the
319
+ * post's file, so a database thrown away costs the record of how yesterday's
320
+ * notification went and nothing else. One row per (post, target), because the
321
+ * question it answers is "where does this post stand with that page" and a
322
+ * resend moves that answer rather than adding a second one.
323
+ */
324
+ export interface SentWebmention {
325
+ /** The post the links were in. */
326
+ readonly slug: string;
327
+ /** The post's absolute URL, which is the `source` that was sent. */
328
+ readonly source: string;
329
+ /** The page that was linked to, which is the `target` that was sent. */
330
+ readonly target: string;
331
+ /** The endpoint it was sent to, or `null` when the target advertised none. */
332
+ readonly endpoint: string | null;
333
+ /** How it went. */
334
+ readonly status: WebmentionSendStatus;
335
+ /** Why it failed, or `null`. */
336
+ readonly error: string | null;
337
+ /** When the attempt was made, as an ISO 8601 instant. */
338
+ readonly attemptedAt: string;
339
+ }
340
+ /** A {@link SentWebmention} before the store has timed it. */
341
+ export type NewSentWebmention = Omit<SentWebmention, 'attemptedAt'> & {
342
+ attemptedAt?: string | undefined;
343
+ };
344
+ /** Where a comment came from. */
345
+ export declare const COMMENT_SOURCES: readonly ['comment', 'webmention'];
346
+ /**
347
+ * One of {@link COMMENT_SOURCES}.
348
+ *
349
+ * `comment` is the form under the post. `webmention` is another site's post
350
+ * pointing at this one (TASK-51), which lands in the same file with the same
351
+ * shape and is told apart only by this.
352
+ */
353
+ export type CommentSource = (typeof COMMENT_SOURCES)[number];
354
+ /** What a comment is: something written, or a wordless pointer at the post. */
355
+ export declare const COMMENT_KINDS: readonly ['reply', 'like', 'boost', 'mention', 'repost'];
356
+ /**
357
+ * One of {@link COMMENT_KINDS}. The same five a conversation knows.
358
+ *
359
+ * `reply`, `like` and `boost` are the fediverse's three. `repost` and
360
+ * `mention` are the webmention vocabulary's two extra: a repost is a boost by
361
+ * another name and is shown with them, and a mention is the one thing neither
362
+ * of the others is — somebody's page pointing at this one without answering
363
+ * it (TASK-51).
364
+ */
365
+ export type CommentKind = (typeof COMMENT_KINDS)[number];
366
+ /** Where a comment stands with the moderator. */
367
+ export declare const COMMENT_STATUSES: readonly ['pending', 'approved', 'spam'];
368
+ /**
369
+ * One of {@link COMMENT_STATUSES}.
370
+ *
371
+ * `pending` is waiting for somebody to look at it, and is the state almost
372
+ * every comment starts in; `approved` is on the page and in the feeds; `spam`
373
+ * is kept rather than deleted, so a checker can be told it was wrong and so a
374
+ * mistake can be undone.
375
+ */
376
+ export type CommentStatus = (typeof COMMENT_STATUSES)[number];
377
+ /** Who wrote a comment, as much as the site knows. */
378
+ export interface CommentAuthor {
379
+ /** The name they gave, which is what the page shows. */
380
+ name: string;
381
+ /** Their website, or `null`. Shown, and marked `nofollow ugc` like any link. */
382
+ url: string | null;
383
+ /**
384
+ * Their email, or `null`. **Never shown**: it is here for the moderator, for
385
+ * the auto-approval rule, and for the spam checker.
386
+ */
387
+ email: string | null;
388
+ /**
389
+ * Their avatar, or `null`.
256
390
  *
257
- * Throws {@link DuplicateUsernameError} when the name is taken.
391
+ * Only a webmention ever has one, out of the `u-photo` of the source page's
392
+ * `h-card` (TASK-51): a form asks for no picture, and one it did ask for
393
+ * would be a stranger's file on somebody else's page. It is a URL on the
394
+ * author's own site, printed as an `<img>` exactly as a fediverse reply's is.
258
395
  */
259
- createUser(input: CreateUserInput): User;
396
+ avatar: string | null;
397
+ }
398
+ /** What a comment says, in both the forms the site keeps. */
399
+ export interface CommentContent {
400
+ /** What was typed, which is what a person wrote and the source of truth. */
401
+ markdown: string;
402
+ /** {@link CommentContent.markdown} through the restricted profile. */
403
+ html: string;
404
+ }
405
+ /**
406
+ * One comment, as its post's file under `content/_data/comments/` holds it.
407
+ *
408
+ * The shape is deliberately wider than a form submission. A webmention
409
+ * (TASK-51) is somebody else's post pointing at this one: it has a `url` and no
410
+ * email, it may be a like rather than a reply, and it belongs in the same file
411
+ * and the same thread. So every entry says its `source` and its `kind`, and
412
+ * nothing assumes a person filled in a form.
413
+ */
414
+ export interface CommentRecord {
415
+ /** Its name, unique across the site, and what a reply puts in `inReplyTo`. */
416
+ id: string;
417
+ /** Where it came from. */
418
+ source: CommentSource;
419
+ /** What it is. */
420
+ kind: CommentKind;
421
+ /** Where it stands with the moderator. */
422
+ status: CommentStatus;
423
+ /** Who wrote it. */
424
+ author: CommentAuthor;
425
+ /** What it says. */
426
+ content: CommentContent;
427
+ /** When it was submitted, as an ISO 8601 instant. */
428
+ submitted: string;
260
429
  /**
261
- * The user, when the password is theirs; `undefined` when it is not, or when
262
- * there is no such user. The two failures are deliberately indistinguishable.
430
+ * A salted hash of the address it came from, or `null`.
431
+ *
432
+ * The address itself is not kept: the file is published with the site and
433
+ * goes into git, and an IP address there would be a reader's home written
434
+ * into a public repository. The hash is enough to see that two comments came
435
+ * from one place, which is what a moderator is actually asking.
263
436
  */
264
- verifyPassword(username: string, password: string): User | undefined;
437
+ addressHash: string | null;
438
+ /** The comment it answers, or `null` for one answering the post itself. */
439
+ inReplyTo: string | null;
265
440
  /**
266
- * Hash a new password and put it on a user, replacing the old one. Returns
267
- * `false` when there is no such user. Sessions are left alone; deciding
268
- * which of them a password change should end is the caller's business.
441
+ * Where it lives when it lives somewhere else: the source page of a
442
+ * webmention, and `null` for a comment written on this site.
443
+ *
444
+ * It is what a later webmention from the same page is recognised by, which
445
+ * is how one updates the comment it made before instead of adding a second,
446
+ * and it is where the thread's permalink points for an entry this site did
447
+ * not host.
269
448
  */
270
- setPassword(userId: number, password: string): boolean;
449
+ url: string | null;
271
450
  /**
272
- * Delete a user. Their sessions go with them, because the foreign key
273
- * cascades. Returns `false` when there was nothing to delete.
451
+ * Whether whoever wrote it asked to hear about replies to it (TASK-55).
452
+ *
453
+ * Only ever true for a comment that carries an `author.email`, because there
454
+ * would be nowhere to send it otherwise, and only ever set by somebody
455
+ * ticking the box on the form — a webmention has nobody to ask. What it buys
456
+ * is one message when a reply to this comment is approved, carrying an
457
+ * unsubscribe link that needs no login.
274
458
  */
275
- deleteUser(userId: number): boolean;
459
+ notify: boolean;
460
+ }
461
+ /**
462
+ * A {@link CommentRecord} with the post it is on.
463
+ *
464
+ * The file it lives in is named after the post's slug and records the post's
465
+ * permalink, so both are facts of the file rather than of the entry; the index
466
+ * carries them on the row because a query by id has no file to read them from.
467
+ */
468
+ export interface PostComment extends CommentRecord {
469
+ /** The post's slug, which names the file the comment lives in. */
470
+ slug: string;
471
+ /** That post's permalink, for the moderation screen and the feeds. */
472
+ permalink: string;
473
+ }
474
+ /** How {@link AdminStore.listComments} narrows and pages. */
475
+ export interface ListCommentsOptions extends ListPageOptions {
476
+ /** Only comments at this status. Every status when it is not given. */
477
+ status?: CommentStatus | undefined;
478
+ }
479
+ /**
480
+ * Everything the database holds that is not the content index: the sessions,
481
+ * the followers and inbox indexes, the delivery outcomes, the relay handshake
482
+ * and the scheduler's watermark.
483
+ *
484
+ * All of it is a cache (decision-9). The followers and the inbox rows are read
485
+ * back from `content/_data/federation/` on every boot; the rest a site is free
486
+ * to lose, and the README says what losing it costs. It is deliberately not
487
+ * part of the {@link ContentStore}: the two share the file, one database per
488
+ * site with a migration ledger each, and nothing else.
489
+ */
490
+ export interface AdminStore {
491
+ /** Absolute path of the SQLite file, the same one the content index uses. */
492
+ readonly file: string;
276
493
  /** Start a session and hand back its id and CSRF token. */
277
494
  createSession(input: CreateSessionInput): Session;
278
495
  /**
@@ -294,35 +511,102 @@ export interface AdminStore {
294
511
  except?: string;
295
512
  }): number;
296
513
  /**
297
- * How many settings are stored. Zero on a site that has never saved the
298
- * settings form, which is what decides whether to seed from
299
- * `content/_data/site.json`.
514
+ * Issue a password reset link and hand back its token, which is the only
515
+ * time the token exists outside the link.
300
516
  */
301
- countSettings(): number;
517
+ createPasswordReset(input: CreatePasswordResetInput): IssuedPasswordReset;
302
518
  /**
303
- * Every stored setting, by key. The store keeps no opinion about what the
304
- * keys mean or what they are worth; `readSiteSettings` in `settings.ts` is
305
- * what turns them into a typed shape with defaults.
519
+ * What a token entitles its holder to, or `undefined` when it entitles them
520
+ * to nothing: no such token, or one that has expired. An expired row is
521
+ * deleted on the way past, as an expired session's is.
306
522
  */
307
- allSettings(): Record<string, string>;
523
+ getPasswordReset(token: string, now?: Date): PasswordReset | undefined;
524
+ /** Spend a token. Returns `false` when there was nothing to spend. */
525
+ deletePasswordReset(token: string): boolean;
308
526
  /**
309
- * Write settings, in one transaction, leaving keys not named alone. An
310
- * existing key is replaced.
527
+ * Cancel every reset a user has outstanding, and say how many went.
528
+ *
529
+ * What a finished reset calls: the link that was used is spent, and so is
530
+ * every other one asked for in the meantime, because whoever set the
531
+ * password now owns the account and the others are only ways back in for
532
+ * somebody who does not.
311
533
  */
312
- setSettings(values: Record<string, string>): void;
534
+ deletePasswordResetsForUser(userId: number): number;
535
+ /** Delete every expired reset. Returns how many went. */
536
+ prunePasswordResets(now?: Date): number;
313
537
  /**
314
- * Every key pair an actor holds, in {@link ACTOR_KEY_ALGORITHMS} order, so a
315
- * caller handing them to Fedify gets HTTP Signatures first whatever order
316
- * they were written in. Empty on a site that has not federated yet.
538
+ * Spend a one-click link's token, and say whether it had anything left.
539
+ *
540
+ * `true` the first time and `false` for ever after, which is what makes a
541
+ * link in an email single use (TASK-55). Only the token's SHA-256 is kept,
542
+ * as a password reset's is, so a copy of this file is not a list of the
543
+ * links a site has sent. `expiresAt` is the moment the token's own signature
544
+ * stops meaning anything, and is only there so the row can be swept.
317
545
  */
318
- listActorKeys(identifier: string): ActorKey[];
546
+ spendToken(token: string, expiresAt: string, now?: Date): boolean;
547
+ /** Delete every spent token that has expired. Returns how many went. */
548
+ pruneSpentTokens(now?: Date): number;
319
549
  /**
320
- * Store one key pair, replacing whatever that actor held for the same
321
- * algorithm. Rotating a key is therefore a write rather than a migration.
550
+ * The rows of the settings table an older version of this CMS wrote, or
551
+ * `undefined` when the table is gone — which it is on every site that has
552
+ * booted this version once.
553
+ *
554
+ * Settings live in `content/_data/site.json` now (decision-9). This is the
555
+ * one read left of the table they used to live in, so the first boot after
556
+ * the upgrade can write them out; `migrateSettingsToFile` in `settings.ts`
557
+ * is what decides what they mean.
322
558
  */
323
- putActorKey(key: NewActorKey): ActorKey;
324
- /** How many actors follow the site. What the followers collection counts. */
325
- countFollowers(): number;
559
+ legacySettings(): LegacySetting[] | undefined;
560
+ /**
561
+ * The rows of the actor key table an older version of this CMS wrote, or
562
+ * `undefined` when the table is gone — which it is on every site that has
563
+ * booted this version once.
564
+ *
565
+ * Key pairs live in `data/keys` now (decision-9). This is the one read left
566
+ * of the table they used to live in, and the reason it exists at all is that
567
+ * losing an actor's private key is the one loss a federated site cannot
568
+ * recover from; `migrateActorKeysToFiles` in `federation/keys.ts` writes them
569
+ * out before the table is dropped.
570
+ */
571
+ legacyActorKeys(): LegacyActorKey[] | undefined;
572
+ /**
573
+ * The rows of the users table an older version of this CMS wrote, or
574
+ * `undefined` when the table is gone — which it is on every site that has
575
+ * booted this version once.
576
+ *
577
+ * Accounts live in `data/users.json` now (decision-9). This is the one read
578
+ * left of the table they used to live in, so the first boot after the
579
+ * upgrade can write them out; `migrateUsersToFile` in `accounts.ts` is what
580
+ * does it, ids and all, so a session that names a user still finds them.
581
+ */
582
+ legacyUsers(): LegacyUser[] | undefined;
583
+ /**
584
+ * Drop a table whose contents this version keeps in files instead.
585
+ *
586
+ * Called once the rows have been written out, and safe when the table is
587
+ * already gone. It is deliberately not a schema migration: a migration runs
588
+ * when the database is opened, which is before anything knows where the
589
+ * content directory is, and dropping the rows before they are written out is
590
+ * the one mistake this milestone cannot make.
591
+ */
592
+ dropLegacyTable(name: string): void;
593
+ /**
594
+ * One piece of the CMS's own operational state, or `undefined`.
595
+ *
596
+ * Not a setting: nothing here is a site's choice and nothing here belongs in
597
+ * `content/_data/site.json`, which is where a setting lives. It is a cache
598
+ * like the rest of the database, and losing it costs a site nothing it
599
+ * cannot work out again. The scheduler's watermark — how far through the
600
+ * calendar it has got — lives here.
601
+ */
602
+ getState(key: string): string | undefined;
603
+ /** Write one piece of that state. */
604
+ setState(key: string, value: string): void;
605
+ /**
606
+ * How many actors follow one user, or the whole site when no user is named.
607
+ * What a user's followers collection counts.
608
+ */
609
+ countFollowers(username?: string): number;
326
610
  /**
327
611
  * Followers, newest follow first, optionally one page of them.
328
612
  *
@@ -331,17 +615,26 @@ export interface AdminStore {
331
615
  * arrives while somebody is walking the collection shifts the pages under
332
616
  * them by one, which is the same trade the outbox makes.
333
617
  */
334
- listFollowers(options?: ListPageOptions): Follower[];
335
- /** One follower by actor id, or `undefined`. */
336
- getFollower(actorId: string): Follower | undefined;
618
+ listFollowers(username?: string, options?: ListPageOptions): Follower[];
619
+ /** One user's follower by actor id, or `undefined`. */
620
+ getFollower(username: string, actorId: string): Follower | undefined;
337
621
  /**
338
622
  * Store a follower, replacing whatever was known about that actor. The
339
623
  * original `followedAt` is kept, because a repeat `Follow` from an actor
340
624
  * that already follows is a redelivery rather than a new follow.
341
625
  */
342
626
  putFollower(follower: NewFollower): Follower;
343
- /** Forget a follower. Returns `false` when there was nothing to forget. */
344
- deleteFollower(actorId: string): boolean;
627
+ /** Forget one user's follower. Returns `false` when there was nothing to forget. */
628
+ deleteFollower(username: string, actorId: string): boolean;
629
+ /**
630
+ * Make the followers index say exactly this, in one transaction.
631
+ *
632
+ * What a rebuild from every `content/_data/federation/{username}/followers.json` calls
633
+ * (decision-9): the file is the source, so a row it does not carry is a row
634
+ * that should not be delivered to, and replacing the lot is the only way to
635
+ * say that. Nothing outside a rebuild should reach for it.
636
+ */
637
+ replaceFollowers(followers: readonly NewFollower[]): void;
345
638
  /** How many activities the inbound log holds. */
346
639
  countInboxActivities(): number;
347
640
  /** Logged activities, newest first, optionally one page of them. */
@@ -353,27 +646,140 @@ export interface AdminStore {
353
646
  * against.
354
647
  */
355
648
  logInboxActivity(activity: NewInboxActivity): InboxActivity;
356
- /** How many activities this site has sent. */
357
- countOutboundActivities(): number;
358
- /** Activities this site sent, newest first, optionally one page of them. */
359
- listOutboundActivities(options?: ListPageOptions): OutboundActivity[];
360
- /** One sent activity by its id, or `undefined`. */
361
- getOutboundActivity(activityId: string): OutboundActivity | undefined;
362
649
  /**
363
- * Record an activity on its way out, leaving the original `created_at` alone
364
- * so a redelivery does not pretend the activity is new.
650
+ * Make the inbound log index say exactly this, in one transaction, with the
651
+ * row ids starting again from one.
652
+ *
653
+ * The counterpart of {@link AdminStore.replaceFollowers} over
654
+ * `content/_data/federation/inbox/{yyyy}-{mm}.jsonl`. The ids restart
655
+ * because a row id is the order things arrived in and nothing else: letting
656
+ * them climb on every boot would give the same log different ids on two
657
+ * machines holding the same files.
365
658
  */
366
- putOutboundActivity(activity: NewOutboundActivity): OutboundActivity;
659
+ replaceInboxActivities(activities: readonly NewInboxActivity[]): void;
660
+ /** How many logged activities are replies to anything at all. */
661
+ countReplies(): number;
662
+ /** Every logged reply, newest first, optionally one page of them. */
663
+ listReplies(options?: ListPageOptions): InboxActivity[];
664
+ /** How many replies one object — a post's ActivityStreams id — has. */
665
+ countRepliesTo(objectId: string): number;
666
+ /** One object's replies, newest first, optionally one page of them. */
667
+ listRepliesTo(objectId: string, options?: ListPageOptions): InboxActivity[];
668
+ /**
669
+ * Every logged activity that names one of these objects — as what it is
670
+ * about, or as what it answers — oldest first.
671
+ *
672
+ * The query a conversation is built from: the likes and boosts of a post,
673
+ * the replies to it, and the `Delete` or `Undo` that takes one of those
674
+ * back, all name an object this side already knows the id of. Both columns
675
+ * are read because a `Create` is *about* the note it carries and only
676
+ * `in_reply_to` says which post that note answers.
677
+ *
678
+ * Oldest first, because that is the order a thread reads in; a caller
679
+ * wanting the newest can reverse a page it has. Naming nothing returns
680
+ * nothing rather than everything.
681
+ */
682
+ listActivitiesAbout(objectIds: readonly string[]): InboxActivity[];
683
+ /** One post's comments, oldest first, whatever status they are at. */
684
+ listCommentsFor(slug: string): PostComment[];
685
+ /**
686
+ * How many of one post's comments stand at one status.
687
+ *
688
+ * A count rather than a list because that is what the feeds want: the RSS
689
+ * `source:comments` element carries a number per item, and reading every
690
+ * comment on every post of a feed page to arrive at it would be a page of
691
+ * text for a page of integers.
692
+ */
693
+ countCommentsFor(slug: string, status: CommentStatus): number;
694
+ /**
695
+ * Comments across the site, newest first, optionally of one status and one
696
+ * page of them. What the moderation screen and the site-wide feed read.
697
+ */
698
+ listComments(options: ListCommentsOptions): PostComment[];
699
+ /** One comment by id, or `undefined`. */
700
+ getComment(id: string): PostComment | undefined;
701
+ /** How many comments stand at each status. What the dashboard shows. */
702
+ countCommentsByStatus(): Record<CommentStatus, number>;
703
+ /**
704
+ * Whether this name and email have had a comment approved before.
705
+ *
706
+ * WordPress's rule, and the whole of the auto-approval decision: somebody a
707
+ * moderator has already let through does not queue again. Both have to
708
+ * match, so a stranger typing a regular's name is still held.
709
+ */
710
+ hasApprovedAuthor(name: string, email: string | null): boolean;
711
+ /** Store a comment, replacing whatever was known about that id. */
712
+ putComment(comment: PostComment): PostComment;
713
+ /** Forget a comment. Returns `false` when there was nothing to forget. */
714
+ deleteComment(id: string): boolean;
715
+ /**
716
+ * Make the comment index say exactly this, in one transaction.
717
+ *
718
+ * What a rebuild from `content/_data/comments/` calls (decision-9): the
719
+ * files are the source, so a row they do not carry is a comment that should
720
+ * not be on the page. Nothing outside a rebuild should reach for it.
721
+ */
722
+ replaceComments(comments: readonly PostComment[]): void;
367
723
  /**
368
724
  * Record how one delivery to one follower ended, replacing the previous
369
725
  * outcome for that pair: the table answers "where does this activity stand
370
- * with each follower", which a redelivery moves rather than adds to.
726
+ * with each follower", which a resend moves rather than adds to.
371
727
  */
372
728
  recordDelivery(delivery: NewDelivery): Delivery;
373
729
  /** Every follower's outcome for one activity, in follower order. */
374
730
  listDeliveries(activityId: string): Delivery[];
375
731
  /** How many followers this activity stands at each status with. */
376
732
  countDeliveriesByStatus(activityId: string): Record<DeliveryStatus, number>;
733
+ /**
734
+ * The most recent delivery to one inbox, whichever activity it carried, or
735
+ * `undefined` when nothing has ever gone there.
736
+ *
737
+ * Keyed by the inbox rather than by the actor because that is what a relay
738
+ * has: it is not a follower, so there is no follower row to look its
739
+ * outcomes up by, and the inbox is the thing the site was told to deliver to.
740
+ */
741
+ lastDeliveryToInbox(inboxId: string): Delivery | undefined;
742
+ /**
743
+ * The most recent delivery about one object — a post's ActivityStreams id,
744
+ * or the site actor's — or `undefined` when nothing about it has ever gone
745
+ * out.
746
+ *
747
+ * What the federation screen's per-post row is filled in from. Keyed by the
748
+ * object rather than by the activity because a post is a series of
749
+ * activities and the row is about the post: the newest outcome is the one
750
+ * that says where the post currently stands with the fediverse.
751
+ */
752
+ lastDeliveryToObject(objectId: string): Delivery | undefined;
753
+ /**
754
+ * Record how one webmention to one page ended, replacing the previous
755
+ * outcome for that (post, target): the table answers "where does this post
756
+ * stand with that page", which a resend moves rather than adds to.
757
+ */
758
+ recordSentWebmention(sent: NewSentWebmention): SentWebmention;
759
+ /** What one post's links came to, target by target, in target order. */
760
+ listSentWebmentions(slug: string): SentWebmention[];
761
+ /** How many of one post's targets stand at each status. */
762
+ countSentWebmentionsByStatus(slug: string): Record<WebmentionSendStatus, number>;
763
+ /** Every relay subscription, oldest first, however it stands. */
764
+ listRelays(): Relay[];
765
+ /** One relay subscription by the inbox it was made to, or `undefined`. */
766
+ getRelay(inboxId: string): Relay | undefined;
767
+ /**
768
+ * The subscription one `Follow` established, or `undefined`.
769
+ *
770
+ * This is how an `Accept` or a `Reject` arriving later finds the relay it
771
+ * answers: the activity names the follow, and the follow names the record.
772
+ */
773
+ getRelayByFollow(followId: string): Relay | undefined;
774
+ /**
775
+ * Store a relay subscription, replacing whatever was known about that inbox
776
+ * and leaving the original `createdAt` alone: a subscription that moves from
777
+ * pending to accepted is the same subscription, and when it began is a fact
778
+ * only the row remembers.
779
+ */
780
+ putRelay(relay: NewRelay): Relay;
781
+ /** Forget a relay subscription. Returns `false` when there was nothing to forget. */
782
+ deleteRelay(inboxId: string): boolean;
377
783
  /** Delete every expired session. Returns how many went. */
378
784
  pruneSessions(now?: Date): number;
379
785
  /**
@@ -389,13 +795,6 @@ export interface AdminStore {
389
795
  /** Close the database. Safe to call twice. */
390
796
  close(): void;
391
797
  }
392
- /** Thrown when a username is already taken. Usernames are the login key. */
393
- export declare class DuplicateUsernameError extends Error {
394
- readonly name = "DuplicateUsernameError";
395
- /** The name that was already in use. */
396
- readonly username: string;
397
- constructor(username: string);
398
- }
399
798
  /**
400
799
  * Open (and if needed create) the admin tables in `dataDir`, applying every
401
800
  * migration the package ships. Applying them is idempotent.
@@ -404,4 +803,59 @@ export declare class DuplicateUsernameError extends Error {
404
803
  * index holding a second one on the same file.
405
804
  */
406
805
  export declare function openAdminStore(options: OpenAdminStoreOptions): AdminStore;
806
+ /**
807
+ * One row of the settings table TASK-14 wrote, for the one boot that reads it.
808
+ *
809
+ * Settings live in `content/_data/site.json` now (decision-9), so this shape
810
+ * exists only so `migrateSettingsToFile` can write the rows out before the
811
+ * table goes. `updatedAt` is what tells a row written by the settings screen
812
+ * from a `site.json` a person edited afterwards.
813
+ */
814
+ export interface LegacySetting {
815
+ /** The setting's name, e.g. `title`. */
816
+ key: string;
817
+ /** Its value, always a string; numbers and lists were spelled as text. */
818
+ value: string;
819
+ /** When it was last written, as an ISO instant. */
820
+ updatedAt: string;
821
+ }
822
+ /**
823
+ * One row of the actor key table TASK-16 wrote, for the one boot that reads it.
824
+ *
825
+ * Key pairs live in `data/keys` as JWK files now (decision-9), so this shape
826
+ * exists only so `migrateActorKeysToFiles` can write the rows out before the
827
+ * table goes. The public half the table also held is not here: it is derived
828
+ * from the private one, and reading it would only invite the two to disagree.
829
+ */
830
+ export interface LegacyActorKey {
831
+ /** Which actor the pair belonged to: the sentinel identifier, not a handle. */
832
+ identifier: string;
833
+ /**
834
+ * Which algorithm it is for, as the table spelled it. A bare string rather
835
+ * than one of the algorithms this version knows, on purpose: a row a later
836
+ * version wrote is still a key that must reach a file rather than go with
837
+ * the table.
838
+ */
839
+ algorithm: string;
840
+ /** The private key as JWK, JSON encoded. What the file ends up holding. */
841
+ privateJwk: string;
842
+ }
843
+ /**
844
+ * One row of the users table TASK-9 wrote, for the one boot that reads it.
845
+ *
846
+ * Accounts live in `data/users.json` now (decision-9), so this shape exists
847
+ * only so `migrateUsersToFile` can write the rows out before the table goes.
848
+ * The id comes with them: a session in the same database names its user by it,
849
+ * and a migration that renumbered everybody would sign the whole site out.
850
+ */
851
+ export interface LegacyUser {
852
+ /** The row id, which the file keeps. */
853
+ id: number;
854
+ /** The login name. */
855
+ username: string;
856
+ /** The PHC-encoded argon2id hash, moved into the file as it stands. */
857
+ passwordHash: string;
858
+ /** When the user was created, as an ISO instant. */
859
+ createdAt: string;
860
+ }
407
861
  //# sourceMappingURL=store.d.ts.map