@geekity/cms 0.3.0 → 0.5.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 (660) hide show
  1. package/README.md +2217 -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/{layouts/document-editor.njk → pages/documents/editor.njk} +75 -59
  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/federation/settings.njk +77 -0
  19. package/admin/pages/media/library.njk +160 -0
  20. package/admin/pages/messages/all.njk +110 -0
  21. package/admin/pages/navigation/menus.njk +129 -0
  22. package/admin/{layouts → pages}/placeholder.njk +5 -1
  23. package/admin/pages/settings/discussion.njk +99 -0
  24. package/admin/pages/settings/email.njk +203 -0
  25. package/admin/pages/settings/general.njk +29 -0
  26. package/admin/pages/settings/permalinks.njk +50 -0
  27. package/admin/pages/settings/reading.njk +64 -0
  28. package/admin/pages/tools/content-index.njk +89 -0
  29. package/admin/pages/users/edit.njk +239 -0
  30. package/admin/pages/users/list.njk +51 -0
  31. package/admin/pages/users/new.njk +41 -0
  32. package/admin/static/admin.css +558 -3
  33. package/admin/static/copy.js +52 -0
  34. package/admin/static/editor.js +16 -16
  35. package/admin/static/slug.js +61 -0
  36. package/dist/access-log.d.ts +44 -0
  37. package/dist/access-log.d.ts.map +1 -0
  38. package/dist/access-log.js +71 -0
  39. package/dist/access-log.js.map +1 -0
  40. package/dist/admin/accounts.d.ts +413 -0
  41. package/dist/admin/accounts.d.ts.map +1 -0
  42. package/dist/admin/accounts.js +680 -0
  43. package/dist/admin/accounts.js.map +1 -0
  44. package/dist/admin/appearance.d.ts +80 -0
  45. package/dist/admin/appearance.d.ts.map +1 -0
  46. package/dist/admin/appearance.js +136 -0
  47. package/dist/admin/appearance.js.map +1 -0
  48. package/dist/admin/comments.d.ts +90 -0
  49. package/dist/admin/comments.d.ts.map +1 -0
  50. package/dist/admin/comments.js +244 -0
  51. package/dist/admin/comments.js.map +1 -0
  52. package/dist/admin/credentials.d.ts +26 -2
  53. package/dist/admin/credentials.d.ts.map +1 -1
  54. package/dist/admin/credentials.js +35 -2
  55. package/dist/admin/credentials.js.map +1 -1
  56. package/dist/admin/documents.d.ts +117 -10
  57. package/dist/admin/documents.d.ts.map +1 -1
  58. package/dist/admin/documents.js +328 -41
  59. package/dist/admin/documents.js.map +1 -1
  60. package/dist/admin/federation.d.ts +161 -50
  61. package/dist/admin/federation.d.ts.map +1 -1
  62. package/dist/admin/federation.js +235 -93
  63. package/dist/admin/federation.js.map +1 -1
  64. package/dist/admin/formatting.d.ts +20 -0
  65. package/dist/admin/formatting.d.ts.map +1 -0
  66. package/dist/admin/formatting.js +72 -0
  67. package/dist/admin/formatting.js.map +1 -0
  68. package/dist/admin/headers.d.ts +65 -0
  69. package/dist/admin/headers.d.ts.map +1 -0
  70. package/dist/admin/headers.js +104 -0
  71. package/dist/admin/headers.js.map +1 -0
  72. package/dist/admin/index.d.ts +38 -12
  73. package/dist/admin/index.d.ts.map +1 -1
  74. package/dist/admin/index.js +25 -7
  75. package/dist/admin/index.js.map +1 -1
  76. package/dist/admin/media.d.ts +168 -0
  77. package/dist/admin/media.d.ts.map +1 -0
  78. package/dist/admin/media.js +295 -0
  79. package/dist/admin/media.js.map +1 -0
  80. package/dist/admin/menu.d.ts +79 -0
  81. package/dist/admin/menu.d.ts.map +1 -0
  82. package/dist/admin/menu.js +143 -0
  83. package/dist/admin/menu.js.map +1 -0
  84. package/dist/admin/messages.d.ts +80 -0
  85. package/dist/admin/messages.d.ts.map +1 -0
  86. package/dist/admin/messages.js +152 -0
  87. package/dist/admin/messages.js.map +1 -0
  88. package/dist/admin/navigation.d.ts +155 -0
  89. package/dist/admin/navigation.d.ts.map +1 -0
  90. package/dist/admin/navigation.js +292 -0
  91. package/dist/admin/navigation.js.map +1 -0
  92. package/dist/admin/preview.d.ts.map +1 -1
  93. package/dist/admin/preview.js +31 -7
  94. package/dist/admin/preview.js.map +1 -1
  95. package/dist/admin/recovery.d.ts +92 -0
  96. package/dist/admin/recovery.d.ts.map +1 -0
  97. package/dist/admin/recovery.js +229 -0
  98. package/dist/admin/recovery.js.map +1 -0
  99. package/dist/admin/routes.d.ts +0 -20
  100. package/dist/admin/routes.d.ts.map +1 -1
  101. package/dist/admin/routes.js +164 -105
  102. package/dist/admin/routes.js.map +1 -1
  103. package/dist/admin/session.d.ts +17 -3
  104. package/dist/admin/session.d.ts.map +1 -1
  105. package/dist/admin/session.js +19 -5
  106. package/dist/admin/session.js.map +1 -1
  107. package/dist/admin/settings-discussion.d.ts +37 -0
  108. package/dist/admin/settings-discussion.d.ts.map +1 -0
  109. package/dist/admin/settings-discussion.js +117 -0
  110. package/dist/admin/settings-discussion.js.map +1 -0
  111. package/dist/admin/settings-email.d.ts +54 -0
  112. package/dist/admin/settings-email.d.ts.map +1 -0
  113. package/dist/admin/settings-email.js +189 -0
  114. package/dist/admin/settings-email.js.map +1 -0
  115. package/dist/admin/settings-federation.d.ts +48 -0
  116. package/dist/admin/settings-federation.d.ts.map +1 -0
  117. package/dist/admin/settings-federation.js +123 -0
  118. package/dist/admin/settings-federation.js.map +1 -0
  119. package/dist/admin/settings-general.d.ts +12 -0
  120. package/dist/admin/settings-general.d.ts.map +1 -0
  121. package/dist/admin/settings-general.js +48 -0
  122. package/dist/admin/settings-general.js.map +1 -0
  123. package/dist/admin/settings-page.d.ts +119 -0
  124. package/dist/admin/settings-page.d.ts.map +1 -0
  125. package/dist/admin/settings-page.js +132 -0
  126. package/dist/admin/settings-page.js.map +1 -0
  127. package/dist/admin/settings-pages.d.ts +30 -0
  128. package/dist/admin/settings-pages.d.ts.map +1 -0
  129. package/dist/admin/settings-pages.js +43 -0
  130. package/dist/admin/settings-pages.js.map +1 -0
  131. package/dist/admin/settings-permalinks.d.ts +16 -0
  132. package/dist/admin/settings-permalinks.d.ts.map +1 -0
  133. package/dist/admin/settings-permalinks.js +32 -0
  134. package/dist/admin/settings-permalinks.js.map +1 -0
  135. package/dist/admin/settings-reading.d.ts +34 -0
  136. package/dist/admin/settings-reading.d.ts.map +1 -0
  137. package/dist/admin/settings-reading.js +49 -0
  138. package/dist/admin/settings-reading.js.map +1 -0
  139. package/dist/admin/settings.d.ts +339 -96
  140. package/dist/admin/settings.d.ts.map +1 -1
  141. package/dist/admin/settings.js +600 -308
  142. package/dist/admin/settings.js.map +1 -1
  143. package/dist/admin/store.d.ts +606 -152
  144. package/dist/admin/store.d.ts.map +1 -1
  145. package/dist/admin/store.js +863 -268
  146. package/dist/admin/store.js.map +1 -1
  147. package/dist/admin/taxonomy.d.ts +114 -0
  148. package/dist/admin/taxonomy.d.ts.map +1 -0
  149. package/dist/admin/taxonomy.js +289 -0
  150. package/dist/admin/taxonomy.js.map +1 -0
  151. package/dist/admin/templates.d.ts +46 -14
  152. package/dist/admin/templates.d.ts.map +1 -1
  153. package/dist/admin/templates.js +46 -14
  154. package/dist/admin/templates.js.map +1 -1
  155. package/dist/admin/throttle.d.ts +84 -0
  156. package/dist/admin/throttle.d.ts.map +1 -0
  157. package/dist/admin/throttle.js +143 -0
  158. package/dist/admin/throttle.js.map +1 -0
  159. package/dist/admin/tools.d.ts +111 -0
  160. package/dist/admin/tools.d.ts.map +1 -0
  161. package/dist/admin/tools.js +182 -0
  162. package/dist/admin/tools.js.map +1 -0
  163. package/dist/admin/uploads.d.ts +28 -2
  164. package/dist/admin/uploads.d.ts.map +1 -1
  165. package/dist/admin/uploads.js +43 -4
  166. package/dist/admin/uploads.js.map +1 -1
  167. package/dist/admin/users.d.ts +100 -13
  168. package/dist/admin/users.d.ts.map +1 -1
  169. package/dist/admin/users.js +412 -39
  170. package/dist/admin/users.js.map +1 -1
  171. package/dist/cache.d.ts +141 -0
  172. package/dist/cache.d.ts.map +1 -0
  173. package/dist/cache.js +207 -0
  174. package/dist/cache.js.map +1 -0
  175. package/dist/cli.d.ts +14 -2
  176. package/dist/cli.d.ts.map +1 -1
  177. package/dist/cli.js +429 -21
  178. package/dist/cli.js.map +1 -1
  179. package/dist/comments/akismet.d.ts +130 -0
  180. package/dist/comments/akismet.d.ts.map +1 -0
  181. package/dist/comments/akismet.js +274 -0
  182. package/dist/comments/akismet.js.map +1 -0
  183. package/dist/comments/conversation.d.ts +29 -0
  184. package/dist/comments/conversation.d.ts.map +1 -0
  185. package/dist/comments/conversation.js +60 -0
  186. package/dist/comments/conversation.js.map +1 -0
  187. package/dist/comments/form.d.ts +123 -0
  188. package/dist/comments/form.d.ts.map +1 -0
  189. package/dist/comments/form.js +79 -0
  190. package/dist/comments/form.js.map +1 -0
  191. package/dist/comments/index.d.ts +16 -0
  192. package/dist/comments/index.d.ts.map +1 -0
  193. package/dist/comments/index.js +10 -0
  194. package/dist/comments/index.js.map +1 -0
  195. package/dist/comments/markdown.d.ts +8 -0
  196. package/dist/comments/markdown.d.ts.map +1 -0
  197. package/dist/comments/markdown.js +85 -0
  198. package/dist/comments/markdown.js.map +1 -0
  199. package/dist/comments/moderate.d.ts +71 -0
  200. package/dist/comments/moderate.d.ts.map +1 -0
  201. package/dist/comments/moderate.js +88 -0
  202. package/dist/comments/moderate.js.map +1 -0
  203. package/dist/comments/policy.d.ts +57 -0
  204. package/dist/comments/policy.d.ts.map +1 -0
  205. package/dist/comments/policy.js +80 -0
  206. package/dist/comments/policy.js.map +1 -0
  207. package/dist/comments/records.d.ts +266 -0
  208. package/dist/comments/records.d.ts.map +1 -0
  209. package/dist/comments/records.js +487 -0
  210. package/dist/comments/records.js.map +1 -0
  211. package/dist/comments/routes.d.ts +60 -0
  212. package/dist/comments/routes.d.ts.map +1 -0
  213. package/dist/comments/routes.js +212 -0
  214. package/dist/comments/routes.js.map +1 -0
  215. package/dist/comments/submission.d.ts +297 -0
  216. package/dist/comments/submission.d.ts.map +1 -0
  217. package/dist/comments/submission.js +280 -0
  218. package/dist/comments/submission.js.map +1 -0
  219. package/dist/comments/viewer.d.ts +21 -0
  220. package/dist/comments/viewer.d.ts.map +1 -0
  221. package/dist/comments/viewer.js +45 -0
  222. package/dist/comments/viewer.js.map +1 -0
  223. package/dist/config.d.ts +225 -5
  224. package/dist/config.d.ts.map +1 -1
  225. package/dist/config.js +121 -6
  226. package/dist/config.js.map +1 -1
  227. package/dist/contact/delivery.d.ts +60 -0
  228. package/dist/contact/delivery.d.ts.map +1 -0
  229. package/dist/contact/delivery.js +87 -0
  230. package/dist/contact/delivery.js.map +1 -0
  231. package/dist/contact/form.d.ts +130 -0
  232. package/dist/contact/form.d.ts.map +1 -0
  233. package/dist/contact/form.js +163 -0
  234. package/dist/contact/form.js.map +1 -0
  235. package/dist/contact/index.d.ts +17 -0
  236. package/dist/contact/index.d.ts.map +1 -0
  237. package/dist/contact/index.js +13 -0
  238. package/dist/contact/index.js.map +1 -0
  239. package/dist/contact/records.d.ts +124 -0
  240. package/dist/contact/records.d.ts.map +1 -0
  241. package/dist/contact/records.js +218 -0
  242. package/dist/contact/records.js.map +1 -0
  243. package/dist/contact/routes.d.ts +17 -0
  244. package/dist/contact/routes.d.ts.map +1 -0
  245. package/dist/contact/routes.js +127 -0
  246. package/dist/contact/routes.js.map +1 -0
  247. package/dist/contact/submission.d.ts +103 -0
  248. package/dist/contact/submission.d.ts.map +1 -0
  249. package/dist/contact/submission.js +166 -0
  250. package/dist/contact/submission.js.map +1 -0
  251. package/dist/content/document.d.ts +7 -2
  252. package/dist/content/document.d.ts.map +1 -1
  253. package/dist/content/document.js +1 -0
  254. package/dist/content/document.js.map +1 -1
  255. package/dist/content/index.d.ts +7 -2
  256. package/dist/content/index.d.ts.map +1 -1
  257. package/dist/content/index.js +4 -1
  258. package/dist/content/index.js.map +1 -1
  259. package/dist/content/markdown.d.ts.map +1 -1
  260. package/dist/content/markdown.js +24 -2
  261. package/dist/content/markdown.js.map +1 -1
  262. package/dist/content/parser.d.ts.map +1 -1
  263. package/dist/content/parser.js +11 -4
  264. package/dist/content/parser.js.map +1 -1
  265. package/dist/content/save.d.ts +5 -0
  266. package/dist/content/save.d.ts.map +1 -1
  267. package/dist/content/save.js +44 -1
  268. package/dist/content/save.js.map +1 -1
  269. package/dist/content/schedule.d.ts +125 -0
  270. package/dist/content/schedule.d.ts.map +1 -0
  271. package/dist/content/schedule.js +207 -0
  272. package/dist/content/schedule.js.map +1 -0
  273. package/dist/content/search.d.ts +64 -0
  274. package/dist/content/search.d.ts.map +1 -0
  275. package/dist/content/search.js +136 -0
  276. package/dist/content/search.js.map +1 -0
  277. package/dist/content/store.d.ts +220 -7
  278. package/dist/content/store.d.ts.map +1 -1
  279. package/dist/content/store.js +392 -67
  280. package/dist/content/store.js.map +1 -1
  281. package/dist/content/sync.d.ts +7 -1
  282. package/dist/content/sync.d.ts.map +1 -1
  283. package/dist/content/sync.js +18 -10
  284. package/dist/content/sync.js.map +1 -1
  285. package/dist/content/time.d.ts +56 -0
  286. package/dist/content/time.d.ts.map +1 -0
  287. package/dist/content/time.js +141 -0
  288. package/dist/content/time.js.map +1 -0
  289. package/dist/content/writer.d.ts.map +1 -1
  290. package/dist/content/writer.js +3 -0
  291. package/dist/content/writer.js.map +1 -1
  292. package/dist/env.d.ts +70 -5
  293. package/dist/env.d.ts.map +1 -1
  294. package/dist/federation/actor.d.ts +95 -41
  295. package/dist/federation/actor.d.ts.map +1 -1
  296. package/dist/federation/actor.js +180 -53
  297. package/dist/federation/actor.js.map +1 -1
  298. package/dist/federation/article.d.ts +44 -17
  299. package/dist/federation/article.d.ts.map +1 -1
  300. package/dist/federation/article.js +98 -46
  301. package/dist/federation/article.js.map +1 -1
  302. package/dist/federation/delivery.d.ts +63 -15
  303. package/dist/federation/delivery.d.ts.map +1 -1
  304. package/dist/federation/delivery.js +154 -80
  305. package/dist/federation/delivery.js.map +1 -1
  306. package/dist/federation/federation.d.ts +22 -17
  307. package/dist/federation/federation.d.ts.map +1 -1
  308. package/dist/federation/federation.js +98 -77
  309. package/dist/federation/federation.js.map +1 -1
  310. package/dist/federation/followers.d.ts +3 -2
  311. package/dist/federation/followers.d.ts.map +1 -1
  312. package/dist/federation/followers.js +6 -5
  313. package/dist/federation/followers.js.map +1 -1
  314. package/dist/federation/import-wordpress.d.ts +128 -0
  315. package/dist/federation/import-wordpress.d.ts.map +1 -0
  316. package/dist/federation/import-wordpress.js +426 -0
  317. package/dist/federation/import-wordpress.js.map +1 -0
  318. package/dist/federation/inbox.d.ts +36 -13
  319. package/dist/federation/inbox.d.ts.map +1 -1
  320. package/dist/federation/inbox.js +120 -49
  321. package/dist/federation/inbox.js.map +1 -1
  322. package/dist/federation/index.d.ts +22 -10
  323. package/dist/federation/index.d.ts.map +1 -1
  324. package/dist/federation/index.js +13 -8
  325. package/dist/federation/index.js.map +1 -1
  326. package/dist/federation/keys.d.ts +98 -20
  327. package/dist/federation/keys.d.ts.map +1 -1
  328. package/dist/federation/keys.js +269 -39
  329. package/dist/federation/keys.js.map +1 -1
  330. package/dist/federation/mount.d.ts +46 -10
  331. package/dist/federation/mount.d.ts.map +1 -1
  332. package/dist/federation/mount.js +300 -28
  333. package/dist/federation/mount.js.map +1 -1
  334. package/dist/federation/paths.d.ts +23 -35
  335. package/dist/federation/paths.d.ts.map +1 -1
  336. package/dist/federation/paths.js +26 -39
  337. package/dist/federation/paths.js.map +1 -1
  338. package/dist/federation/records.d.ts +223 -0
  339. package/dist/federation/records.d.ts.map +1 -0
  340. package/dist/federation/records.js +506 -0
  341. package/dist/federation/records.js.map +1 -0
  342. package/dist/federation/relays.d.ts +114 -0
  343. package/dist/federation/relays.d.ts.map +1 -0
  344. package/dist/federation/relays.js +290 -0
  345. package/dist/federation/relays.js.map +1 -0
  346. package/dist/federation/replies.d.ts +80 -0
  347. package/dist/federation/replies.d.ts.map +1 -0
  348. package/dist/federation/replies.js +166 -0
  349. package/dist/federation/replies.js.map +1 -0
  350. package/dist/federation/wordpress.d.ts +131 -0
  351. package/dist/federation/wordpress.d.ts.map +1 -0
  352. package/dist/federation/wordpress.js +341 -0
  353. package/dist/federation/wordpress.js.map +1 -0
  354. package/dist/files/atomic.d.ts +103 -0
  355. package/dist/files/atomic.d.ts.map +1 -0
  356. package/dist/files/atomic.js +150 -0
  357. package/dist/files/atomic.js.map +1 -0
  358. package/dist/files/index.d.ts +3 -0
  359. package/dist/files/index.d.ts.map +1 -0
  360. package/dist/files/index.js +2 -0
  361. package/dist/files/index.js.map +1 -0
  362. package/dist/forms/protection.d.ts +85 -0
  363. package/dist/forms/protection.d.ts.map +1 -0
  364. package/dist/forms/protection.js +116 -0
  365. package/dist/forms/protection.js.map +1 -0
  366. package/dist/images/icons.d.ts +59 -0
  367. package/dist/images/icons.d.ts.map +1 -0
  368. package/dist/images/icons.js +131 -0
  369. package/dist/images/icons.js.map +1 -0
  370. package/dist/images/index.d.ts +7 -0
  371. package/dist/images/index.d.ts.map +1 -0
  372. package/dist/images/index.js +4 -0
  373. package/dist/images/index.js.map +1 -0
  374. package/dist/images/markup.d.ts +47 -0
  375. package/dist/images/markup.d.ts.map +1 -0
  376. package/dist/images/markup.js +175 -0
  377. package/dist/images/markup.js.map +1 -0
  378. package/dist/images/paths.d.ts +34 -0
  379. package/dist/images/paths.d.ts.map +1 -0
  380. package/dist/images/paths.js +41 -0
  381. package/dist/images/paths.js.map +1 -0
  382. package/dist/images/variants.d.ts +104 -0
  383. package/dist/images/variants.d.ts.map +1 -0
  384. package/dist/images/variants.js +320 -0
  385. package/dist/images/variants.js.map +1 -0
  386. package/dist/index.d.ts +123 -19
  387. package/dist/index.d.ts.map +1 -1
  388. package/dist/index.js +344 -25
  389. package/dist/index.js.map +1 -1
  390. package/dist/init.d.ts +18 -0
  391. package/dist/init.d.ts.map +1 -1
  392. package/dist/init.js +34 -3
  393. package/dist/init.js.map +1 -1
  394. package/dist/mail/brevo.d.ts +35 -0
  395. package/dist/mail/brevo.d.ts.map +1 -0
  396. package/dist/mail/brevo.js +85 -0
  397. package/dist/mail/brevo.js.map +1 -0
  398. package/dist/mail/credentials.d.ts +62 -0
  399. package/dist/mail/credentials.d.ts.map +1 -0
  400. package/dist/mail/credentials.js +86 -0
  401. package/dist/mail/credentials.js.map +1 -0
  402. package/dist/mail/index.d.ts +15 -0
  403. package/dist/mail/index.d.ts.map +1 -0
  404. package/dist/mail/index.js +8 -0
  405. package/dist/mail/index.js.map +1 -0
  406. package/dist/mail/memory.d.ts +35 -0
  407. package/dist/mail/memory.d.ts.map +1 -0
  408. package/dist/mail/memory.js +29 -0
  409. package/dist/mail/memory.js.map +1 -0
  410. package/dist/mail/provider.d.ts +80 -0
  411. package/dist/mail/provider.d.ts.map +1 -0
  412. package/dist/mail/provider.js +23 -0
  413. package/dist/mail/provider.js.map +1 -0
  414. package/dist/mail/service.d.ts +156 -0
  415. package/dist/mail/service.d.ts.map +1 -0
  416. package/dist/mail/service.js +265 -0
  417. package/dist/mail/service.js.map +1 -0
  418. package/dist/mail/smtp.d.ts +39 -0
  419. package/dist/mail/smtp.d.ts.map +1 -0
  420. package/dist/mail/smtp.js +58 -0
  421. package/dist/mail/smtp.js.map +1 -0
  422. package/dist/mail/templates.d.ts +91 -0
  423. package/dist/mail/templates.d.ts.map +1 -0
  424. package/dist/mail/templates.js +69 -0
  425. package/dist/mail/templates.js.map +1 -0
  426. package/dist/notifications/comments.d.ts +66 -0
  427. package/dist/notifications/comments.d.ts.map +1 -0
  428. package/dist/notifications/comments.js +151 -0
  429. package/dist/notifications/comments.js.map +1 -0
  430. package/dist/notifications/digest.d.ts +135 -0
  431. package/dist/notifications/digest.d.ts.map +1 -0
  432. package/dist/notifications/digest.js +287 -0
  433. package/dist/notifications/digest.js.map +1 -0
  434. package/dist/notifications/index.d.ts +13 -0
  435. package/dist/notifications/index.d.ts.map +1 -0
  436. package/dist/notifications/index.js +8 -0
  437. package/dist/notifications/index.js.map +1 -0
  438. package/dist/notifications/links.d.ts +60 -0
  439. package/dist/notifications/links.d.ts.map +1 -0
  440. package/dist/notifications/links.js +63 -0
  441. package/dist/notifications/links.js.map +1 -0
  442. package/dist/notifications/optouts.d.ts +48 -0
  443. package/dist/notifications/optouts.d.ts.map +1 -0
  444. package/dist/notifications/optouts.js +93 -0
  445. package/dist/notifications/optouts.js.map +1 -0
  446. package/dist/notifications/preferences.d.ts +158 -0
  447. package/dist/notifications/preferences.d.ts.map +1 -0
  448. package/dist/notifications/preferences.js +195 -0
  449. package/dist/notifications/preferences.js.map +1 -0
  450. package/dist/notifications/routes.d.ts +28 -0
  451. package/dist/notifications/routes.d.ts.map +1 -0
  452. package/dist/notifications/routes.js +184 -0
  453. package/dist/notifications/routes.js.map +1 -0
  454. package/dist/notifications/tokens.d.ts +60 -0
  455. package/dist/notifications/tokens.d.ts.map +1 -0
  456. package/dist/notifications/tokens.js +125 -0
  457. package/dist/notifications/tokens.js.map +1 -0
  458. package/dist/notify.d.ts +84 -0
  459. package/dist/notify.d.ts.map +1 -0
  460. package/dist/notify.js +149 -0
  461. package/dist/notify.js.map +1 -0
  462. package/dist/web/archive.d.ts +49 -0
  463. package/dist/web/archive.d.ts.map +1 -0
  464. package/dist/web/archive.js +59 -0
  465. package/dist/web/archive.js.map +1 -0
  466. package/dist/web/assets.d.ts +6 -5
  467. package/dist/web/assets.d.ts.map +1 -1
  468. package/dist/web/assets.js +5 -7
  469. package/dist/web/assets.js.map +1 -1
  470. package/dist/web/authors.d.ts +143 -0
  471. package/dist/web/authors.d.ts.map +1 -0
  472. package/dist/web/authors.js +178 -0
  473. package/dist/web/authors.js.map +1 -0
  474. package/dist/web/comments.d.ts +60 -0
  475. package/dist/web/comments.d.ts.map +1 -0
  476. package/dist/web/comments.js +207 -0
  477. package/dist/web/comments.js.map +1 -0
  478. package/dist/web/context.d.ts +162 -28
  479. package/dist/web/context.d.ts.map +1 -1
  480. package/dist/web/context.js +123 -26
  481. package/dist/web/context.js.map +1 -1
  482. package/dist/web/conversation.d.ts +209 -0
  483. package/dist/web/conversation.d.ts.map +1 -0
  484. package/dist/web/conversation.js +557 -0
  485. package/dist/web/conversation.js.map +1 -0
  486. package/dist/web/documents.d.ts +36 -6
  487. package/dist/web/documents.d.ts.map +1 -1
  488. package/dist/web/documents.js +52 -10
  489. package/dist/web/documents.js.map +1 -1
  490. package/dist/web/feed-atom.d.ts +22 -0
  491. package/dist/web/feed-atom.d.ts.map +1 -0
  492. package/dist/web/feed-atom.js +83 -0
  493. package/dist/web/feed-atom.js.map +1 -0
  494. package/dist/web/feed-item.d.ts +145 -0
  495. package/dist/web/feed-item.d.ts.map +1 -0
  496. package/dist/web/feed-item.js +120 -0
  497. package/dist/web/feed-item.js.map +1 -0
  498. package/dist/web/feed-json.d.ts +79 -0
  499. package/dist/web/feed-json.d.ts.map +1 -0
  500. package/dist/web/feed-json.js +53 -0
  501. package/dist/web/feed-json.js.map +1 -0
  502. package/dist/web/feed-rss.d.ts +26 -0
  503. package/dist/web/feed-rss.d.ts.map +1 -0
  504. package/dist/web/feed-rss.js +203 -0
  505. package/dist/web/feed-rss.js.map +1 -0
  506. package/dist/web/feed-source.d.ts +244 -0
  507. package/dist/web/feed-source.d.ts.map +1 -0
  508. package/dist/web/feed-source.js +201 -0
  509. package/dist/web/feed-source.js.map +1 -0
  510. package/dist/web/feed-xml.d.ts +63 -0
  511. package/dist/web/feed-xml.d.ts.map +1 -0
  512. package/dist/web/feed-xml.js +116 -0
  513. package/dist/web/feed-xml.js.map +1 -0
  514. package/dist/web/feeds.d.ts +31 -96
  515. package/dist/web/feeds.d.ts.map +1 -1
  516. package/dist/web/feeds.js +94 -198
  517. package/dist/web/feeds.js.map +1 -1
  518. package/dist/web/health.d.ts +31 -0
  519. package/dist/web/health.d.ts.map +1 -0
  520. package/dist/web/health.js +44 -0
  521. package/dist/web/health.js.map +1 -0
  522. package/dist/web/index.d.ts +30 -10
  523. package/dist/web/index.d.ts.map +1 -1
  524. package/dist/web/index.js +18 -7
  525. package/dist/web/index.js.map +1 -1
  526. package/dist/web/navigation.d.ts +195 -0
  527. package/dist/web/navigation.d.ts.map +1 -0
  528. package/dist/web/navigation.js +300 -0
  529. package/dist/web/navigation.js.map +1 -0
  530. package/dist/web/negotiate.d.ts +31 -0
  531. package/dist/web/negotiate.d.ts.map +1 -1
  532. package/dist/web/negotiate.js +31 -11
  533. package/dist/web/negotiate.js.map +1 -1
  534. package/dist/web/recent.d.ts +43 -0
  535. package/dist/web/recent.d.ts.map +1 -0
  536. package/dist/web/recent.js +37 -0
  537. package/dist/web/recent.js.map +1 -0
  538. package/dist/web/render.d.ts +203 -9
  539. package/dist/web/render.d.ts.map +1 -1
  540. package/dist/web/render.js +341 -17
  541. package/dist/web/render.js.map +1 -1
  542. package/dist/web/routes.d.ts +25 -8
  543. package/dist/web/routes.d.ts.map +1 -1
  544. package/dist/web/routes.js +854 -93
  545. package/dist/web/routes.js.map +1 -1
  546. package/dist/web/sanitize.d.ts +19 -0
  547. package/dist/web/sanitize.d.ts.map +1 -0
  548. package/dist/web/sanitize.js +272 -0
  549. package/dist/web/sanitize.js.map +1 -0
  550. package/dist/web/search.d.ts +79 -0
  551. package/dist/web/search.d.ts.map +1 -0
  552. package/dist/web/search.js +94 -0
  553. package/dist/web/search.js.map +1 -0
  554. package/dist/web/sitemap.d.ts +120 -0
  555. package/dist/web/sitemap.d.ts.map +1 -0
  556. package/dist/web/sitemap.js +199 -0
  557. package/dist/web/sitemap.js.map +1 -0
  558. package/dist/web/taxonomy.d.ts +152 -0
  559. package/dist/web/taxonomy.d.ts.map +1 -0
  560. package/dist/web/taxonomy.js +237 -0
  561. package/dist/web/taxonomy.js.map +1 -0
  562. package/dist/web/templates.d.ts +54 -16
  563. package/dist/web/templates.d.ts.map +1 -1
  564. package/dist/web/templates.js +109 -28
  565. package/dist/web/templates.js.map +1 -1
  566. package/dist/web/themes.d.ts +226 -0
  567. package/dist/web/themes.d.ts.map +1 -0
  568. package/dist/web/themes.js +314 -0
  569. package/dist/web/themes.js.map +1 -0
  570. package/dist/webmention/discovery.d.ts +51 -0
  571. package/dist/webmention/discovery.d.ts.map +1 -0
  572. package/dist/webmention/discovery.js +176 -0
  573. package/dist/webmention/discovery.js.map +1 -0
  574. package/dist/webmention/html.d.ts +71 -0
  575. package/dist/webmention/html.d.ts.map +1 -0
  576. package/dist/webmention/html.js +285 -0
  577. package/dist/webmention/html.js.map +1 -0
  578. package/dist/webmention/index.d.ts +12 -0
  579. package/dist/webmention/index.d.ts.map +1 -0
  580. package/dist/webmention/index.js +8 -0
  581. package/dist/webmention/index.js.map +1 -0
  582. package/dist/webmention/links.d.ts +18 -0
  583. package/dist/webmention/links.d.ts.map +1 -0
  584. package/dist/webmention/links.js +64 -0
  585. package/dist/webmention/links.js.map +1 -0
  586. package/dist/webmention/microformats.d.ts +93 -0
  587. package/dist/webmention/microformats.d.ts.map +1 -0
  588. package/dist/webmention/microformats.js +376 -0
  589. package/dist/webmention/microformats.js.map +1 -0
  590. package/dist/webmention/receive.d.ts +141 -0
  591. package/dist/webmention/receive.d.ts.map +1 -0
  592. package/dist/webmention/receive.js +264 -0
  593. package/dist/webmention/receive.js.map +1 -0
  594. package/dist/webmention/routes.d.ts +37 -0
  595. package/dist/webmention/routes.d.ts.map +1 -0
  596. package/dist/webmention/routes.js +75 -0
  597. package/dist/webmention/routes.js.map +1 -0
  598. package/dist/webmention/service.d.ts +94 -0
  599. package/dist/webmention/service.d.ts.map +1 -0
  600. package/dist/webmention/service.js +207 -0
  601. package/dist/webmention/service.js.map +1 -0
  602. package/package.json +11 -6
  603. package/templates/site/content/_data/site.json +8 -0
  604. package/templates/site/content/pages/about.md +7 -0
  605. package/templates/site/content/posts/2026-01-01-hello-world.md +5 -3
  606. package/templates/site/geekity.config.ts +11 -1
  607. package/templates/site/tsconfig.json +1 -1
  608. package/themes/default/README.md +1216 -50
  609. package/themes/default/layouts/404.njk +2 -3
  610. package/themes/default/layouts/author.njk +36 -0
  611. package/themes/default/layouts/base.njk +161 -31
  612. package/themes/default/layouts/category.njk +27 -0
  613. package/themes/default/layouts/front-page.njk +56 -0
  614. package/themes/default/layouts/home.njk +17 -1
  615. package/themes/default/layouts/page.njk +57 -5
  616. package/themes/default/layouts/post.njk +78 -19
  617. package/themes/default/layouts/search.njk +57 -0
  618. package/themes/default/layouts/tag.njk +3 -6
  619. package/themes/default/mail/comment-digest.html.njk +37 -0
  620. package/themes/default/mail/comment-digest.subject.njk +3 -0
  621. package/themes/default/mail/comment-digest.txt.njk +40 -0
  622. package/themes/default/mail/comment-pending.html.njk +27 -0
  623. package/themes/default/mail/comment-pending.subject.njk +2 -0
  624. package/themes/default/mail/comment-pending.txt.njk +24 -0
  625. package/themes/default/mail/comment-reply.html.njk +21 -0
  626. package/themes/default/mail/comment-reply.subject.njk +2 -0
  627. package/themes/default/mail/comment-reply.txt.njk +22 -0
  628. package/themes/default/mail/contact-message.html.njk +24 -0
  629. package/themes/default/mail/contact-message.subject.njk +5 -0
  630. package/themes/default/mail/contact-message.txt.njk +20 -0
  631. package/themes/default/mail/password-changed.html.njk +18 -0
  632. package/themes/default/mail/password-changed.subject.njk +2 -0
  633. package/themes/default/mail/password-changed.txt.njk +17 -0
  634. package/themes/default/mail/password-reset.html.njk +22 -0
  635. package/themes/default/mail/password-reset.subject.njk +2 -0
  636. package/themes/default/mail/password-reset.txt.njk +18 -0
  637. package/themes/default/mail/test.html.njk +14 -0
  638. package/themes/default/mail/test.subject.njk +2 -0
  639. package/themes/default/mail/test.txt.njk +12 -0
  640. package/themes/default/partials/archive.njk +27 -0
  641. package/themes/default/partials/bio.njk +71 -0
  642. package/themes/default/partials/comment-form.njk +137 -0
  643. package/themes/default/partials/contact-form.njk +88 -0
  644. package/themes/default/partials/conversation.njk +139 -0
  645. package/themes/default/partials/feeds.njk +26 -0
  646. package/themes/default/partials/jsonld.njk +120 -0
  647. package/themes/default/partials/menu.njk +32 -0
  648. package/themes/default/partials/pagination.njk +12 -6
  649. package/themes/default/partials/post-list.njk +51 -18
  650. package/themes/default/partials/search-form.njk +22 -0
  651. package/themes/default/partials/tags.njk +26 -5
  652. package/themes/default/static/highlight.js +4 -0
  653. package/themes/default/static/style.css +1142 -194
  654. package/themes/default/theme.json +9 -0
  655. package/admin/layouts/federation.njk +0 -145
  656. package/admin/layouts/login.njk +0 -23
  657. package/admin/layouts/settings.njk +0 -116
  658. package/admin/layouts/setup.njk +0 -27
  659. package/admin/layouts/users.njk +0 -91
  660. /package/admin/{layouts/_flash.njk → components/flash.njk} +0 -0
@@ -0,0 +1,143 @@
1
+ import type { ProfileLink, User } from '../admin/accounts.ts';
2
+ import type { FeedFormat } from './feed-source.ts';
3
+ /**
4
+ * Where a person is on this site: their archive, their feeds, and which user
5
+ * a post's `author` names.
6
+ *
7
+ * decision-14 makes every user an actor at `{baseUrl}/author/{username}/`, and
8
+ * an actor id has to be a URL that already means something — so the author URL
9
+ * is a page first (TASK-67) and an actor second (TASK-68). Everything that
10
+ * builds or reads one of those URLs goes through this module, so the archive,
11
+ * its feeds, the byline a theme prints and, later, the dispatcher Fedify
12
+ * registers cannot disagree about where a person lives.
13
+ *
14
+ * The base is a constant rather than a setting, unlike the taxonomy bases: it
15
+ * is an actor id, and an id that moved when somebody edited a settings field
16
+ * would be a different account to every follower holding it. `author` is a
17
+ * reserved top-level path for exactly that reason.
18
+ */
19
+ /** The first URL segment every author archive, feed and actor lives under. */
20
+ export declare const AUTHOR_BASE = "author";
21
+ /** Where the shared inbox goes (decision-14), and so the second reserved path. */
22
+ export declare const INBOX_BASE = "inbox";
23
+ /** One author archive, and which page of it. */
24
+ export interface AuthorRequest {
25
+ /** The username the URL named. Not yet known to be a user. */
26
+ username: string;
27
+ /** Zero-based index of the page. */
28
+ pageNumber: number;
29
+ }
30
+ /**
31
+ * The URL of a page of one user's archive, by zero-based index.
32
+ *
33
+ * The one place the shape is spelled, so the routes, the pager, the canonical
34
+ * redirect, the byline a theme prints and the actor id all agree. The first
35
+ * page is the archive root itself, exactly as `/page/1/` collapses onto `/`.
36
+ */
37
+ export declare function authorHref(username: string, index?: number): string;
38
+ /**
39
+ * The URL of one format of one user's feed.
40
+ *
41
+ * WordPress's layout, which decision-14 keeps: the feeds are children of the
42
+ * archive, so `/author/ada/feed/` is RSS 2.0 and the other two hang beside it.
43
+ */
44
+ export declare function authorFeedHref(username: string, format: FeedFormat): string;
45
+ /**
46
+ * A path as the author archive it names, or `undefined` when it names none.
47
+ *
48
+ * The inverse of {@link authorHref}, and deliberately ignorant of whether the
49
+ * username is a user: the caller looks that up and 404s. Only the slashed form
50
+ * parses; the bare `/author/ada` is handled by the canonical redirect, like
51
+ * every other missing trailing slash on the site — which is also where a
52
+ * browser that followed an actor id without its slash lands, because Fedify
53
+ * 404s that form and falls through (doc-8).
54
+ *
55
+ * `page/1/` parses to the first page, which lives at the archive root, so the
56
+ * caller can collapse it the way `/page/1/` collapses onto `/`.
57
+ */
58
+ export declare function parseAuthorPath(pathname: string): AuthorRequest | undefined;
59
+ /**
60
+ * The user a document's `author` names, or `undefined` when it names nobody.
61
+ *
62
+ * Two rules, in this order, and the order is the whole of it. A username is
63
+ * what doc-2 says the key holds, and it is matched exactly, as a login is. A
64
+ * display name is what files written before decision-14 hold — the demo's own
65
+ * posts say `Andrew Shell` — and one that exactly one user answers to reads as
66
+ * that user, so an existing site's posts get their bylines and their archives
67
+ * without anybody rewriting a file. Exactly one: two people called the same
68
+ * thing is not an attribution, and guessing between them would put somebody
69
+ * else's post on somebody's archive.
70
+ *
71
+ * The username rule winning means a file saying `ada` is Ada's post even if
72
+ * somebody else has typed `ada` into their display name, which is the safe way
73
+ * round: a login is unique and a display name is anything at all.
74
+ *
75
+ * The mapping is read rather than written. A file keeps whatever it says until
76
+ * the editor next saves it, and the editor writes a username.
77
+ */
78
+ export declare function userForAuthor(users: readonly User[], author: string | undefined): User | undefined;
79
+ /**
80
+ * Every stored `author` string that reads as this user, for the query behind
81
+ * their archive.
82
+ *
83
+ * The inverse of {@link userForAuthor}, and computed by asking it rather than
84
+ * by restating its rules: a candidate counts only when it resolves back to
85
+ * this user, so a display name another user's username has taken, and one two
86
+ * users share, are both left out without this function having to know why.
87
+ */
88
+ export declare function authorNames(users: readonly User[], user: User): string[];
89
+ /** What a theme is given as `author`, on a post and on an archive alike. */
90
+ export interface AuthorContext {
91
+ /** Their login, when the name resolved to a user. Absent when it did not. */
92
+ username?: string | undefined;
93
+ /** What to print: their display name, else their username, else the raw name. */
94
+ name: string;
95
+ /** Their archive, when the name resolved to a user. Absent when it did not. */
96
+ url?: string | undefined;
97
+ /** What they say about themselves, when they have said anything. */
98
+ bio?: string | undefined;
99
+ /** Their picture, as the path or URL it is served at. */
100
+ avatar?: string | undefined;
101
+ /** What they do, when their profile says. */
102
+ jobTitle?: string | undefined;
103
+ /** Where they are, as they wrote it. */
104
+ location?: string | undefined;
105
+ /** Somewhere else they are, in the order they listed them. */
106
+ links?: readonly ProfileLink[] | undefined;
107
+ }
108
+ /**
109
+ * One user as a theme sees them.
110
+ *
111
+ * `name` is always something printable, so a byline never renders empty: a
112
+ * user who has written no display name is called by their username, which is
113
+ * what their archive is under anyway.
114
+ */
115
+ export declare function profileContext(user: User): AuthorContext;
116
+ /**
117
+ * The profile behind the site's `author` setting, or `undefined` when it names
118
+ * nobody this site has.
119
+ *
120
+ * What a theme is given as `siteAuthor` on a page that is about nobody in
121
+ * particular (decision-16): the site's own identity, the same object a byline
122
+ * is, so the visible h-card, the footer's `rel="me"` links and the structured
123
+ * data all read one profile and cannot drift.
124
+ *
125
+ * Strict where {@link authorContext} is forgiving, and deliberately: a byline
126
+ * prints the name a file gives whether or not anybody answers to it, because
127
+ * somebody did write the post. A site author that resolves to nobody has
128
+ * nothing behind it — no picture, no bio, nowhere to link — so it is absent
129
+ * rather than a lone name, and a theme writes `{% if siteAuthor %}` once.
130
+ */
131
+ export declare function siteAuthorContext(users: readonly User[], author: string | undefined): AuthorContext | undefined;
132
+ /**
133
+ * What a document's `author` puts on the template context, or `undefined` when
134
+ * it names nobody at all.
135
+ *
136
+ * Always an object rather than sometimes a string, which is the change a theme
137
+ * sees (TASK-67): `author.name` is what to print and `author.url` is where to
138
+ * link, and a name resolving to no user has the first and not the second. A
139
+ * theme therefore writes one thing — `{% if author.url %}` — instead of
140
+ * working out whether the front matter happened to hold a login.
141
+ */
142
+ export declare function authorContext(users: readonly User[], author: string | undefined): AuthorContext | undefined;
143
+ //# sourceMappingURL=authors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authors.d.ts","sourceRoot":"","sources":["../../src/web/authors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD;;;;;;;;;;;;;;;GAeG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,WAAW,WAAW,CAAC;AAEpC,kFAAkF;AAClF,eAAO,MAAM,UAAU,UAAU,CAAC;AAElC,gDAAgD;AAChD,MAAM,WAAW,aAAa;IAC5B,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,MAAM,CAG9D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAE3E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAe3E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB,IAAI,GAAG,SAAS,CASlB;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CASxE;AAED,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,8DAA8D;IAC9D,KAAK,CAAC,EAAE,SAAS,WAAW,EAAE,GAAG,SAAS,CAAC;CAC5C;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,aAAa,CAaxD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB,aAAa,GAAG,SAAS,CAG3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB,aAAa,GAAG,SAAS,CAM3B"}
@@ -0,0 +1,178 @@
1
+ import { feedPathUnder } from './feed-source.js';
2
+ import { PAGE_SEGMENT } from './taxonomy.js';
3
+ /**
4
+ * Where a person is on this site: their archive, their feeds, and which user
5
+ * a post's `author` names.
6
+ *
7
+ * decision-14 makes every user an actor at `{baseUrl}/author/{username}/`, and
8
+ * an actor id has to be a URL that already means something — so the author URL
9
+ * is a page first (TASK-67) and an actor second (TASK-68). Everything that
10
+ * builds or reads one of those URLs goes through this module, so the archive,
11
+ * its feeds, the byline a theme prints and, later, the dispatcher Fedify
12
+ * registers cannot disagree about where a person lives.
13
+ *
14
+ * The base is a constant rather than a setting, unlike the taxonomy bases: it
15
+ * is an actor id, and an id that moved when somebody edited a settings field
16
+ * would be a different account to every follower holding it. `author` is a
17
+ * reserved top-level path for exactly that reason.
18
+ */
19
+ /** The first URL segment every author archive, feed and actor lives under. */
20
+ export const AUTHOR_BASE = 'author';
21
+ /** Where the shared inbox goes (decision-14), and so the second reserved path. */
22
+ export const INBOX_BASE = 'inbox';
23
+ /**
24
+ * The URL of a page of one user's archive, by zero-based index.
25
+ *
26
+ * The one place the shape is spelled, so the routes, the pager, the canonical
27
+ * redirect, the byline a theme prints and the actor id all agree. The first
28
+ * page is the archive root itself, exactly as `/page/1/` collapses onto `/`.
29
+ */
30
+ export function authorHref(username, index = 0) {
31
+ const root = `/${AUTHOR_BASE}/${encodeURIComponent(username)}/`;
32
+ return index === 0 ? root : `${root}${PAGE_SEGMENT}/${String(index + 1)}/`;
33
+ }
34
+ /**
35
+ * The URL of one format of one user's feed.
36
+ *
37
+ * WordPress's layout, which decision-14 keeps: the feeds are children of the
38
+ * archive, so `/author/ada/feed/` is RSS 2.0 and the other two hang beside it.
39
+ */
40
+ export function authorFeedHref(username, format) {
41
+ return feedPathUnder(authorHref(username), format);
42
+ }
43
+ /**
44
+ * A path as the author archive it names, or `undefined` when it names none.
45
+ *
46
+ * The inverse of {@link authorHref}, and deliberately ignorant of whether the
47
+ * username is a user: the caller looks that up and 404s. Only the slashed form
48
+ * parses; the bare `/author/ada` is handled by the canonical redirect, like
49
+ * every other missing trailing slash on the site — which is also where a
50
+ * browser that followed an actor id without its slash lands, because Fedify
51
+ * 404s that form and falls through (doc-8).
52
+ *
53
+ * `page/1/` parses to the first page, which lives at the archive root, so the
54
+ * caller can collapse it the way `/page/1/` collapses onto `/`.
55
+ */
56
+ export function parseAuthorPath(pathname) {
57
+ if (!pathname.endsWith('/'))
58
+ return undefined;
59
+ const segments = pathname.split('/').filter((segment) => segment !== '');
60
+ if (segments[0] !== AUTHOR_BASE)
61
+ return undefined;
62
+ const username = segments[1];
63
+ if (username === undefined || username === '')
64
+ return undefined;
65
+ if (segments.length === 2)
66
+ return { username, pageNumber: 0 };
67
+ if (segments.length !== 4 || segments[2] !== PAGE_SEGMENT)
68
+ return undefined;
69
+ const requested = segments[3] ?? '';
70
+ if (!/^[0-9]+$/.test(requested) || Number(requested) < 1)
71
+ return undefined;
72
+ return { username, pageNumber: Number(requested) - 1 };
73
+ }
74
+ /**
75
+ * The user a document's `author` names, or `undefined` when it names nobody.
76
+ *
77
+ * Two rules, in this order, and the order is the whole of it. A username is
78
+ * what doc-2 says the key holds, and it is matched exactly, as a login is. A
79
+ * display name is what files written before decision-14 hold — the demo's own
80
+ * posts say `Andrew Shell` — and one that exactly one user answers to reads as
81
+ * that user, so an existing site's posts get their bylines and their archives
82
+ * without anybody rewriting a file. Exactly one: two people called the same
83
+ * thing is not an attribution, and guessing between them would put somebody
84
+ * else's post on somebody's archive.
85
+ *
86
+ * The username rule winning means a file saying `ada` is Ada's post even if
87
+ * somebody else has typed `ada` into their display name, which is the safe way
88
+ * round: a login is unique and a display name is anything at all.
89
+ *
90
+ * The mapping is read rather than written. A file keeps whatever it says until
91
+ * the editor next saves it, and the editor writes a username.
92
+ */
93
+ export function userForAuthor(users, author) {
94
+ const wanted = (author ?? '').trim();
95
+ if (wanted === '')
96
+ return undefined;
97
+ const byUsername = users.find((user) => user.username === wanted);
98
+ if (byUsername !== undefined)
99
+ return byUsername;
100
+ const named = users.filter((user) => user.profile?.displayName === wanted);
101
+ return named.length === 1 ? named[0] : undefined;
102
+ }
103
+ /**
104
+ * Every stored `author` string that reads as this user, for the query behind
105
+ * their archive.
106
+ *
107
+ * The inverse of {@link userForAuthor}, and computed by asking it rather than
108
+ * by restating its rules: a candidate counts only when it resolves back to
109
+ * this user, so a display name another user's username has taken, and one two
110
+ * users share, are both left out without this function having to know why.
111
+ */
112
+ export function authorNames(users, user) {
113
+ const names = [];
114
+ for (const candidate of [user.username, user.profile?.displayName]) {
115
+ if (candidate === undefined || candidate === '' || names.includes(candidate))
116
+ continue;
117
+ if (userForAuthor(users, candidate)?.id === user.id)
118
+ names.push(candidate);
119
+ }
120
+ return names;
121
+ }
122
+ /**
123
+ * One user as a theme sees them.
124
+ *
125
+ * `name` is always something printable, so a byline never renders empty: a
126
+ * user who has written no display name is called by their username, which is
127
+ * what their archive is under anyway.
128
+ */
129
+ export function profileContext(user) {
130
+ const { profile } = user;
131
+ return {
132
+ username: user.username,
133
+ name: profile?.displayName ?? user.username,
134
+ url: authorHref(user.username),
135
+ ...(profile?.bio === undefined ? {} : { bio: profile.bio }),
136
+ ...(profile?.avatar === undefined ? {} : { avatar: profile.avatar }),
137
+ ...(profile?.jobTitle === undefined ? {} : { jobTitle: profile.jobTitle }),
138
+ ...(profile?.location === undefined ? {} : { location: profile.location }),
139
+ ...(profile?.links === undefined ? {} : { links: profile.links }),
140
+ };
141
+ }
142
+ /**
143
+ * The profile behind the site's `author` setting, or `undefined` when it names
144
+ * nobody this site has.
145
+ *
146
+ * What a theme is given as `siteAuthor` on a page that is about nobody in
147
+ * particular (decision-16): the site's own identity, the same object a byline
148
+ * is, so the visible h-card, the footer's `rel="me"` links and the structured
149
+ * data all read one profile and cannot drift.
150
+ *
151
+ * Strict where {@link authorContext} is forgiving, and deliberately: a byline
152
+ * prints the name a file gives whether or not anybody answers to it, because
153
+ * somebody did write the post. A site author that resolves to nobody has
154
+ * nothing behind it — no picture, no bio, nowhere to link — so it is absent
155
+ * rather than a lone name, and a theme writes `{% if siteAuthor %}` once.
156
+ */
157
+ export function siteAuthorContext(users, author) {
158
+ const user = userForAuthor(users, author);
159
+ return user === undefined ? undefined : profileContext(user);
160
+ }
161
+ /**
162
+ * What a document's `author` puts on the template context, or `undefined` when
163
+ * it names nobody at all.
164
+ *
165
+ * Always an object rather than sometimes a string, which is the change a theme
166
+ * sees (TASK-67): `author.name` is what to print and `author.url` is where to
167
+ * link, and a name resolving to no user has the first and not the second. A
168
+ * theme therefore writes one thing — `{% if author.url %}` — instead of
169
+ * working out whether the front matter happened to hold a login.
170
+ */
171
+ export function authorContext(users, author) {
172
+ const name = (author ?? '').trim();
173
+ if (name === '')
174
+ return undefined;
175
+ const user = userForAuthor(users, name);
176
+ return user === undefined ? { name } : profileContext(user);
177
+ }
178
+ //# sourceMappingURL=authors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authors.js","sourceRoot":"","sources":["../../src/web/authors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import type { AdminStore } from '../admin/store.ts';
2
+ import type { Document } from '../content/document.ts';
3
+ import type { ContentStore } from '../content/store.ts';
4
+ import type { FeedComment } from './feeds.ts';
5
+ /**
6
+ * The comments a site has, in the two shapes it has them: replies its inbox
7
+ * was sent, and comments people left on the page itself.
8
+ *
9
+ * A fediverse reply names the post it answers by the post's ActivityStreams
10
+ * object id, and lives in the inbox log; a native comment names the post by
11
+ * its slug and lives in `content/_data/comments/` (TASK-50). This module is
12
+ * where the two meet for the feeds: a reader subscribed to a post's comments
13
+ * should hear about an answer whichever door it came in by, and
14
+ * `source:comments` should count what a reader would actually see.
15
+ *
16
+ * Only approved native comments are here. One waiting for a moderator and one
17
+ * filed as spam are both stored and neither is anything to publish.
18
+ */
19
+ /** One comment, with the post it answers. */
20
+ export interface Comment extends FeedComment {
21
+ /** The post it answers, which the site-wide feed names on every item. */
22
+ post: Document;
23
+ }
24
+ /** What the comment queries need to reach: both indexes and the site's origin. */
25
+ export interface CommentContext {
26
+ /** Where the replies are logged. */
27
+ admin: AdminStore;
28
+ /** Where the posts are. */
29
+ store: ContentStore;
30
+ /** The site's public origin, for the object ids. */
31
+ baseUrl: string;
32
+ }
33
+ /**
34
+ * One post's comments, newest first, from both sources.
35
+ *
36
+ * A post that has never been delivered has no object id and so can have no
37
+ * fediverse replies — nothing out there has a name for it yet — but it can
38
+ * still have native ones, which are found by its slug.
39
+ */
40
+ export declare function postComments(context: CommentContext, document: Document, limit: number): FeedComment[];
41
+ /**
42
+ * Every post's comments, newest first, each with the post it answers.
43
+ *
44
+ * One about a post that has since been unpublished or trashed is left out,
45
+ * because the post it is about is not there to read: the feed would be showing
46
+ * a conversation about nothing. That filtering happens after the database has
47
+ * paged, so the pages are read until enough survive.
48
+ */
49
+ export declare function siteComments(context: CommentContext, limit: number): Comment[];
50
+ /**
51
+ * How many comments each of these documents has, by permalink: the fediverse
52
+ * replies and the approved native ones together, which is what a reader
53
+ * following `source:comments` would actually find on the page.
54
+ *
55
+ * Resolved rather than counted on demand, because the number goes into the
56
+ * feed's own validator: a feed whose comment counts moved is a changed feed,
57
+ * and a reader holding the old one should be told so.
58
+ */
59
+ export declare function commentCounts(context: CommentContext, documents: readonly Document[]): Map<string, number>;
60
+ //# sourceMappingURL=comments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../../src/web/comments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,mBAAmB,CAAC;AAEjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAIxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C;;;;;;;;;;;;;GAaG;AAEH,6CAA6C;AAC7C,MAAM,WAAW,OAAQ,SAAQ,WAAW;IAC1C,yEAAyE;IACzE,IAAI,EAAE,QAAQ,CAAC;CAChB;AAYD,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,KAAK,EAAE,UAAU,CAAC;IAClB,2BAA2B;IAC3B,KAAK,EAAE,YAAY,CAAC;IACpB,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,GACZ,WAAW,EAAE,CAYf;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,CAkC9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,SAAS,QAAQ,EAAE,GAC7B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAWrB"}
@@ -0,0 +1,207 @@
1
+ import { commentAnchor } from '../comments/conversation.js';
2
+ import { FEDERATION_PREFIX } from '../federation/paths.js';
3
+ import { replyFrom } from '../federation/replies.js';
4
+ import { activityStreamsId, isPublicDocument } from './documents.js';
5
+ import { absoluteUrl } from './negotiate.js';
6
+ /**
7
+ * How many candidates a site-wide page reads for each one it keeps.
8
+ *
9
+ * Replies to a post that has since been unpublished are dropped after the
10
+ * database has already counted them, so the query has to over-read. Four is
11
+ * generous for a site whose posts mostly stay published and bounded for one
12
+ * whose posts do not.
13
+ */
14
+ const OVERSCAN = 4;
15
+ /**
16
+ * One post's comments, newest first, from both sources.
17
+ *
18
+ * A post that has never been delivered has no object id and so can have no
19
+ * fediverse replies — nothing out there has a name for it yet — but it can
20
+ * still have native ones, which are found by its slug.
21
+ */
22
+ export function postComments(context, document, limit) {
23
+ const objectId = activityStreamsId(document, context.baseUrl);
24
+ const naming = authorNaming(context.admin);
25
+ const federated = objectId === undefined
26
+ ? []
27
+ : context.admin
28
+ .listRepliesTo(objectId, { limit })
29
+ .map((activity) => replyFrom(activity, naming))
30
+ .filter((reply) => reply !== undefined);
31
+ return newestFirst([...federated, ...nativeComments(context, document)], limit);
32
+ }
33
+ /**
34
+ * Every post's comments, newest first, each with the post it answers.
35
+ *
36
+ * One about a post that has since been unpublished or trashed is left out,
37
+ * because the post it is about is not there to read: the feed would be showing
38
+ * a conversation about nothing. That filtering happens after the database has
39
+ * paged, so the pages are read until enough survive.
40
+ */
41
+ export function siteComments(context, limit) {
42
+ const naming = authorNaming(context.admin);
43
+ const posts = new PostsByObjectId(context);
44
+ const comments = [];
45
+ for (let offset = 0; comments.length < limit;) {
46
+ const page = context.admin.listReplies({ limit: limit * OVERSCAN, offset });
47
+ if (page.length === 0)
48
+ break;
49
+ offset += page.length;
50
+ for (const activity of page) {
51
+ const reply = replyFrom(activity, naming);
52
+ if (reply === undefined)
53
+ continue;
54
+ const post = posts.get(reply.inReplyTo);
55
+ if (post === undefined)
56
+ continue;
57
+ comments.push({ ...reply, post });
58
+ if (comments.length === limit)
59
+ break;
60
+ }
61
+ }
62
+ // The native comments are over-read for the same reason the replies are: one
63
+ // on a post that has since been unpublished is dropped after the index has
64
+ // already counted it.
65
+ for (const stored of context.admin.listComments({
66
+ status: 'approved',
67
+ limit: limit * OVERSCAN,
68
+ })) {
69
+ const post = context.store.getBySlug(stored.slug);
70
+ if (post === undefined || !isPublicDocument(post, context.store.now()))
71
+ continue;
72
+ comments.push({ ...feedCommentOf(stored, post, context.baseUrl), post });
73
+ }
74
+ return newestFirst(comments, limit);
75
+ }
76
+ /**
77
+ * How many comments each of these documents has, by permalink: the fediverse
78
+ * replies and the approved native ones together, which is what a reader
79
+ * following `source:comments` would actually find on the page.
80
+ *
81
+ * Resolved rather than counted on demand, because the number goes into the
82
+ * feed's own validator: a feed whose comment counts moved is a changed feed,
83
+ * and a reader holding the old one should be told so.
84
+ */
85
+ export function commentCounts(context, documents) {
86
+ const counts = new Map();
87
+ for (const document of documents) {
88
+ const objectId = activityStreamsId(document, context.baseUrl);
89
+ const federated = objectId === undefined ? 0 : context.admin.countRepliesTo(objectId);
90
+ counts.set(document.permalink, federated + context.admin.countCommentsFor(document.slug, 'approved'));
91
+ }
92
+ return counts;
93
+ }
94
+ /** One post's approved native comments, in the shape a feed item wants. */
95
+ function nativeComments(context, document) {
96
+ return context.admin
97
+ .listCommentsFor(document.slug)
98
+ .filter((comment) => comment.status === 'approved')
99
+ .map((comment) => feedCommentOf(comment, document, context.baseUrl));
100
+ }
101
+ /**
102
+ * One stored comment as a feed item.
103
+ *
104
+ * Its `url` is where a reader can actually read it — this page, at the
105
+ * comment's own anchor — and its `id` is the comment's own name, which is what
106
+ * a `guid isPermaLink="false"` is for: a reader that has seen it once does not
107
+ * see it again if the post moves.
108
+ */
109
+ function feedCommentOf(comment, post, baseUrl) {
110
+ return {
111
+ id: comment.id,
112
+ url: `${absoluteUrl(post.permalink, baseUrl)}#${commentAnchor(comment.id)}`,
113
+ author: comment.author.name,
114
+ published: new Date(comment.submitted),
115
+ html: comment.content.html,
116
+ };
117
+ }
118
+ /** The newest `limit` of a mixed list, which is the order every feed shows. */
119
+ function newestFirst(comments, limit) {
120
+ return comments
121
+ .sort((left, right) => right.published.getTime() - left.published.getTime())
122
+ .slice(0, limit);
123
+ }
124
+ /** The name to show for an actor: the follower profile the site holds, if any. */
125
+ function authorNaming(admin) {
126
+ const known = new Map();
127
+ return (actorId) => {
128
+ if (!known.has(actorId)) {
129
+ const follower = admin.getFollower(actorId);
130
+ known.set(actorId, follower?.name ?? follower?.handle ?? undefined);
131
+ }
132
+ return known.get(actorId);
133
+ };
134
+ }
135
+ /**
136
+ * The public post an ActivityStreams object id names, memoised.
137
+ *
138
+ * Almost every id is the one the slug implies, so the slug is tried first and
139
+ * the answer is checked against the document's own id — a post that has been
140
+ * renamed keeps the id it was first delivered under, which no slug spells any
141
+ * more. Only when that fails is the whole index read, once, to find it.
142
+ */
143
+ class PostsByObjectId {
144
+ #context;
145
+ #found = new Map();
146
+ #all;
147
+ constructor(context) {
148
+ this.#context = context;
149
+ }
150
+ get(objectId) {
151
+ if (!this.#found.has(objectId))
152
+ this.#found.set(objectId, this.#resolve(objectId));
153
+ return this.#found.get(objectId);
154
+ }
155
+ #resolve(objectId) {
156
+ const slug = slugOfObjectId(objectId, this.#context.baseUrl);
157
+ if (slug !== undefined) {
158
+ const document = this.#context.store.getBySlug(slug);
159
+ if (document !== undefined &&
160
+ activityStreamsId(document, this.#context.baseUrl) === objectId) {
161
+ return document;
162
+ }
163
+ }
164
+ return this.#everything().get(objectId);
165
+ }
166
+ #everything() {
167
+ if (this.#all === undefined) {
168
+ this.#all = new Map();
169
+ for (const document of this.#context.store.listPosts()) {
170
+ const id = activityStreamsId(document, this.#context.baseUrl);
171
+ if (id !== undefined)
172
+ this.#all.set(id, document);
173
+ }
174
+ }
175
+ return this.#all;
176
+ }
177
+ }
178
+ /**
179
+ * The slug an object id names, when it is one this site would have minted.
180
+ *
181
+ * An id from another host, or one under some other path, belongs to no post
182
+ * here however it is spelled.
183
+ */
184
+ function slugOfObjectId(objectId, baseUrl) {
185
+ let url;
186
+ try {
187
+ url = new URL(objectId);
188
+ }
189
+ catch {
190
+ return undefined;
191
+ }
192
+ if (url.origin !== new URL(baseUrl).origin)
193
+ return undefined;
194
+ const prefix = `${FEDERATION_PREFIX}/posts/`;
195
+ if (!url.pathname.startsWith(prefix))
196
+ return undefined;
197
+ const slug = url.pathname.slice(prefix.length);
198
+ if (slug === '' || slug.includes('/'))
199
+ return undefined;
200
+ try {
201
+ return decodeURIComponent(slug);
202
+ }
203
+ catch {
204
+ return undefined;
205
+ }
206
+ }
207
+ //# sourceMappingURL=comments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comments.js","sourceRoot":"","sources":["../../src/web/comments.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAG5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAErE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAuB7C;;;;;;;GAOG;AACH,MAAM,QAAQ,GAAG,CAAC,CAAC;AAYnB;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,OAAuB,EACvB,QAAkB,EAClB,KAAa;IAEb,MAAM,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,SAAS,GACb,QAAQ,KAAK,SAAS;QACpB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,OAAO,CAAC,KAAK;aACV,aAAa,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC;aAClC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;aAC9C,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;IAEhD,OAAO,WAAW,CAAC,CAAC,GAAG,SAAS,EAAE,GAAG,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAClF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,OAAuB,EAAE,KAAa;IACjE,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,KAAK,GAAG,CAAC;QAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QAC5E,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM;QAC7B,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;QAEtB,KAAK,MAAM,QAAQ,IAAI,IAAI,EAAE,CAAC;YAC5B,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC1C,IAAI,KAAK,KAAK,SAAS;gBAAE,SAAS;YAClC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YACxC,IAAI,IAAI,KAAK,SAAS;gBAAE,SAAS;YAEjC,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAClC,IAAI,QAAQ,CAAC,MAAM,KAAK,KAAK;gBAAE,MAAM;QACvC,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,2EAA2E;IAC3E,sBAAsB;IACtB,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC;QAC9C,MAAM,EAAE,UAAU;QAClB,KAAK,EAAE,KAAK,GAAG,QAAQ;KACxB,CAAC,EAAE,CAAC;QACH,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YAAE,SAAS;QACjF,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAuB,EACvB,SAA8B;IAE9B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9D,MAAM,SAAS,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACtF,MAAM,CAAC,GAAG,CACR,QAAQ,CAAC,SAAS,EAClB,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,CACtE,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,2EAA2E;AAC3E,SAAS,cAAc,CAAC,OAAuB,EAAE,QAAkB;IACjE,OAAO,OAAO,CAAC,KAAK;SACjB,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC;SAC9B,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,KAAK,UAAU,CAAC;SAClD,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;AACzE,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,OAAoB,EAAE,IAAc,EAAE,OAAe;IAC1E,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,GAAG,EAAE,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;QAC3E,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI;QAC3B,SAAS,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;QACtC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI;KAC3B,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,SAAS,WAAW,CAAgC,QAAa,EAAE,KAAa;IAC9E,OAAO,QAAQ;SACZ,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;SAC3E,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACrB,CAAC;AAED,kFAAkF;AAClF,SAAS,YAAY,CAAC,KAAiB;IACrC,MAAM,KAAK,GAAG,IAAI,GAAG,EAA8B,CAAC;IACpD,OAAO,CAAC,OAAO,EAAE,EAAE;QACjB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YAC5C,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,IAAI,SAAS,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,eAAe;IACV,QAAQ,CAAiB;IACzB,MAAM,GAAG,IAAI,GAAG,EAAgC,CAAC;IAC1D,IAAI,CAAoC;IAExC,YAAY,OAAuB;QACjC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC1B,CAAC;IAED,GAAG,CAAC,QAAgB;QAClB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;QACnF,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED,QAAQ,CAAC,QAAgB;QACvB,MAAM,IAAI,GAAG,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC7D,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACrD,IACE,QAAQ,KAAK,SAAS;gBACtB,iBAAiB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,QAAQ,EAC/D,CAAC;gBACD,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED,WAAW;QACT,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;YACtB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC;gBACvD,MAAM,EAAE,GAAG,iBAAiB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;gBAC9D,IAAI,EAAE,KAAK,SAAS;oBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;CACF;AAED;;;;;GAKG;AACH,SAAS,cAAc,CAAC,QAAgB,EAAE,OAAe;IACvD,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAE7D,MAAM,MAAM,GAAG,GAAG,iBAAiB,SAAS,CAAC;IAC7C,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IAEvD,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACxD,IAAI,CAAC;QACH,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}