@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.
- package/README.md +2217 -214
- package/admin/components/fields.njk +165 -0
- package/admin/layouts/base.njk +1 -1
- package/admin/layouts/settings-page.njk +26 -0
- package/admin/layouts/shell.njk +17 -3
- package/admin/pages/account/forgot.njk +47 -0
- package/admin/pages/account/login.njk +26 -0
- package/admin/pages/account/reset.njk +38 -0
- package/admin/pages/account/setup.njk +28 -0
- package/admin/pages/appearance/themes.njk +69 -0
- package/admin/pages/comments/all.njk +133 -0
- package/admin/{layouts/dashboard.njk → pages/dashboard/home.njk} +8 -15
- package/admin/{layouts/document-conflict.njk → pages/documents/conflict.njk} +1 -0
- package/admin/{layouts/document-editor.njk → pages/documents/editor.njk} +75 -59
- package/admin/{layouts/document-list.njk → pages/documents/list.njk} +19 -2
- package/admin/pages/documents/taxonomy.njk +95 -0
- package/admin/pages/federation/followers.njk +238 -0
- package/admin/pages/federation/settings.njk +77 -0
- package/admin/pages/media/library.njk +160 -0
- package/admin/pages/messages/all.njk +110 -0
- package/admin/pages/navigation/menus.njk +129 -0
- package/admin/{layouts → pages}/placeholder.njk +5 -1
- package/admin/pages/settings/discussion.njk +99 -0
- package/admin/pages/settings/email.njk +203 -0
- package/admin/pages/settings/general.njk +29 -0
- package/admin/pages/settings/permalinks.njk +50 -0
- package/admin/pages/settings/reading.njk +64 -0
- package/admin/pages/tools/content-index.njk +89 -0
- package/admin/pages/users/edit.njk +239 -0
- package/admin/pages/users/list.njk +51 -0
- package/admin/pages/users/new.njk +41 -0
- package/admin/static/admin.css +558 -3
- package/admin/static/copy.js +52 -0
- package/admin/static/editor.js +16 -16
- package/admin/static/slug.js +61 -0
- package/dist/access-log.d.ts +44 -0
- package/dist/access-log.d.ts.map +1 -0
- package/dist/access-log.js +71 -0
- package/dist/access-log.js.map +1 -0
- package/dist/admin/accounts.d.ts +413 -0
- package/dist/admin/accounts.d.ts.map +1 -0
- package/dist/admin/accounts.js +680 -0
- package/dist/admin/accounts.js.map +1 -0
- package/dist/admin/appearance.d.ts +80 -0
- package/dist/admin/appearance.d.ts.map +1 -0
- package/dist/admin/appearance.js +136 -0
- package/dist/admin/appearance.js.map +1 -0
- package/dist/admin/comments.d.ts +90 -0
- package/dist/admin/comments.d.ts.map +1 -0
- package/dist/admin/comments.js +244 -0
- package/dist/admin/comments.js.map +1 -0
- package/dist/admin/credentials.d.ts +26 -2
- package/dist/admin/credentials.d.ts.map +1 -1
- package/dist/admin/credentials.js +35 -2
- package/dist/admin/credentials.js.map +1 -1
- package/dist/admin/documents.d.ts +117 -10
- package/dist/admin/documents.d.ts.map +1 -1
- package/dist/admin/documents.js +328 -41
- package/dist/admin/documents.js.map +1 -1
- package/dist/admin/federation.d.ts +161 -50
- package/dist/admin/federation.d.ts.map +1 -1
- package/dist/admin/federation.js +235 -93
- package/dist/admin/federation.js.map +1 -1
- package/dist/admin/formatting.d.ts +20 -0
- package/dist/admin/formatting.d.ts.map +1 -0
- package/dist/admin/formatting.js +72 -0
- package/dist/admin/formatting.js.map +1 -0
- package/dist/admin/headers.d.ts +65 -0
- package/dist/admin/headers.d.ts.map +1 -0
- package/dist/admin/headers.js +104 -0
- package/dist/admin/headers.js.map +1 -0
- package/dist/admin/index.d.ts +38 -12
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +25 -7
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/media.d.ts +168 -0
- package/dist/admin/media.d.ts.map +1 -0
- package/dist/admin/media.js +295 -0
- package/dist/admin/media.js.map +1 -0
- package/dist/admin/menu.d.ts +79 -0
- package/dist/admin/menu.d.ts.map +1 -0
- package/dist/admin/menu.js +143 -0
- package/dist/admin/menu.js.map +1 -0
- package/dist/admin/messages.d.ts +80 -0
- package/dist/admin/messages.d.ts.map +1 -0
- package/dist/admin/messages.js +152 -0
- package/dist/admin/messages.js.map +1 -0
- package/dist/admin/navigation.d.ts +155 -0
- package/dist/admin/navigation.d.ts.map +1 -0
- package/dist/admin/navigation.js +292 -0
- package/dist/admin/navigation.js.map +1 -0
- package/dist/admin/preview.d.ts.map +1 -1
- package/dist/admin/preview.js +31 -7
- package/dist/admin/preview.js.map +1 -1
- package/dist/admin/recovery.d.ts +92 -0
- package/dist/admin/recovery.d.ts.map +1 -0
- package/dist/admin/recovery.js +229 -0
- package/dist/admin/recovery.js.map +1 -0
- package/dist/admin/routes.d.ts +0 -20
- package/dist/admin/routes.d.ts.map +1 -1
- package/dist/admin/routes.js +164 -105
- package/dist/admin/routes.js.map +1 -1
- package/dist/admin/session.d.ts +17 -3
- package/dist/admin/session.d.ts.map +1 -1
- package/dist/admin/session.js +19 -5
- package/dist/admin/session.js.map +1 -1
- package/dist/admin/settings-discussion.d.ts +37 -0
- package/dist/admin/settings-discussion.d.ts.map +1 -0
- package/dist/admin/settings-discussion.js +117 -0
- package/dist/admin/settings-discussion.js.map +1 -0
- package/dist/admin/settings-email.d.ts +54 -0
- package/dist/admin/settings-email.d.ts.map +1 -0
- package/dist/admin/settings-email.js +189 -0
- package/dist/admin/settings-email.js.map +1 -0
- package/dist/admin/settings-federation.d.ts +48 -0
- package/dist/admin/settings-federation.d.ts.map +1 -0
- package/dist/admin/settings-federation.js +123 -0
- package/dist/admin/settings-federation.js.map +1 -0
- package/dist/admin/settings-general.d.ts +12 -0
- package/dist/admin/settings-general.d.ts.map +1 -0
- package/dist/admin/settings-general.js +48 -0
- package/dist/admin/settings-general.js.map +1 -0
- package/dist/admin/settings-page.d.ts +119 -0
- package/dist/admin/settings-page.d.ts.map +1 -0
- package/dist/admin/settings-page.js +132 -0
- package/dist/admin/settings-page.js.map +1 -0
- package/dist/admin/settings-pages.d.ts +30 -0
- package/dist/admin/settings-pages.d.ts.map +1 -0
- package/dist/admin/settings-pages.js +43 -0
- package/dist/admin/settings-pages.js.map +1 -0
- package/dist/admin/settings-permalinks.d.ts +16 -0
- package/dist/admin/settings-permalinks.d.ts.map +1 -0
- package/dist/admin/settings-permalinks.js +32 -0
- package/dist/admin/settings-permalinks.js.map +1 -0
- package/dist/admin/settings-reading.d.ts +34 -0
- package/dist/admin/settings-reading.d.ts.map +1 -0
- package/dist/admin/settings-reading.js +49 -0
- package/dist/admin/settings-reading.js.map +1 -0
- package/dist/admin/settings.d.ts +339 -96
- package/dist/admin/settings.d.ts.map +1 -1
- package/dist/admin/settings.js +600 -308
- package/dist/admin/settings.js.map +1 -1
- package/dist/admin/store.d.ts +606 -152
- package/dist/admin/store.d.ts.map +1 -1
- package/dist/admin/store.js +863 -268
- package/dist/admin/store.js.map +1 -1
- package/dist/admin/taxonomy.d.ts +114 -0
- package/dist/admin/taxonomy.d.ts.map +1 -0
- package/dist/admin/taxonomy.js +289 -0
- package/dist/admin/taxonomy.js.map +1 -0
- package/dist/admin/templates.d.ts +46 -14
- package/dist/admin/templates.d.ts.map +1 -1
- package/dist/admin/templates.js +46 -14
- package/dist/admin/templates.js.map +1 -1
- package/dist/admin/throttle.d.ts +84 -0
- package/dist/admin/throttle.d.ts.map +1 -0
- package/dist/admin/throttle.js +143 -0
- package/dist/admin/throttle.js.map +1 -0
- package/dist/admin/tools.d.ts +111 -0
- package/dist/admin/tools.d.ts.map +1 -0
- package/dist/admin/tools.js +182 -0
- package/dist/admin/tools.js.map +1 -0
- package/dist/admin/uploads.d.ts +28 -2
- package/dist/admin/uploads.d.ts.map +1 -1
- package/dist/admin/uploads.js +43 -4
- package/dist/admin/uploads.js.map +1 -1
- package/dist/admin/users.d.ts +100 -13
- package/dist/admin/users.d.ts.map +1 -1
- package/dist/admin/users.js +412 -39
- package/dist/admin/users.js.map +1 -1
- package/dist/cache.d.ts +141 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +207 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts +14 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +429 -21
- package/dist/cli.js.map +1 -1
- package/dist/comments/akismet.d.ts +130 -0
- package/dist/comments/akismet.d.ts.map +1 -0
- package/dist/comments/akismet.js +274 -0
- package/dist/comments/akismet.js.map +1 -0
- package/dist/comments/conversation.d.ts +29 -0
- package/dist/comments/conversation.d.ts.map +1 -0
- package/dist/comments/conversation.js +60 -0
- package/dist/comments/conversation.js.map +1 -0
- package/dist/comments/form.d.ts +123 -0
- package/dist/comments/form.d.ts.map +1 -0
- package/dist/comments/form.js +79 -0
- package/dist/comments/form.js.map +1 -0
- package/dist/comments/index.d.ts +16 -0
- package/dist/comments/index.d.ts.map +1 -0
- package/dist/comments/index.js +10 -0
- package/dist/comments/index.js.map +1 -0
- package/dist/comments/markdown.d.ts +8 -0
- package/dist/comments/markdown.d.ts.map +1 -0
- package/dist/comments/markdown.js +85 -0
- package/dist/comments/markdown.js.map +1 -0
- package/dist/comments/moderate.d.ts +71 -0
- package/dist/comments/moderate.d.ts.map +1 -0
- package/dist/comments/moderate.js +88 -0
- package/dist/comments/moderate.js.map +1 -0
- package/dist/comments/policy.d.ts +57 -0
- package/dist/comments/policy.d.ts.map +1 -0
- package/dist/comments/policy.js +80 -0
- package/dist/comments/policy.js.map +1 -0
- package/dist/comments/records.d.ts +266 -0
- package/dist/comments/records.d.ts.map +1 -0
- package/dist/comments/records.js +487 -0
- package/dist/comments/records.js.map +1 -0
- package/dist/comments/routes.d.ts +60 -0
- package/dist/comments/routes.d.ts.map +1 -0
- package/dist/comments/routes.js +212 -0
- package/dist/comments/routes.js.map +1 -0
- package/dist/comments/submission.d.ts +297 -0
- package/dist/comments/submission.d.ts.map +1 -0
- package/dist/comments/submission.js +280 -0
- package/dist/comments/submission.js.map +1 -0
- package/dist/comments/viewer.d.ts +21 -0
- package/dist/comments/viewer.d.ts.map +1 -0
- package/dist/comments/viewer.js +45 -0
- package/dist/comments/viewer.js.map +1 -0
- package/dist/config.d.ts +225 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +121 -6
- package/dist/config.js.map +1 -1
- package/dist/contact/delivery.d.ts +60 -0
- package/dist/contact/delivery.d.ts.map +1 -0
- package/dist/contact/delivery.js +87 -0
- package/dist/contact/delivery.js.map +1 -0
- package/dist/contact/form.d.ts +130 -0
- package/dist/contact/form.d.ts.map +1 -0
- package/dist/contact/form.js +163 -0
- package/dist/contact/form.js.map +1 -0
- package/dist/contact/index.d.ts +17 -0
- package/dist/contact/index.d.ts.map +1 -0
- package/dist/contact/index.js +13 -0
- package/dist/contact/index.js.map +1 -0
- package/dist/contact/records.d.ts +124 -0
- package/dist/contact/records.d.ts.map +1 -0
- package/dist/contact/records.js +218 -0
- package/dist/contact/records.js.map +1 -0
- package/dist/contact/routes.d.ts +17 -0
- package/dist/contact/routes.d.ts.map +1 -0
- package/dist/contact/routes.js +127 -0
- package/dist/contact/routes.js.map +1 -0
- package/dist/contact/submission.d.ts +103 -0
- package/dist/contact/submission.d.ts.map +1 -0
- package/dist/contact/submission.js +166 -0
- package/dist/contact/submission.js.map +1 -0
- package/dist/content/document.d.ts +7 -2
- package/dist/content/document.d.ts.map +1 -1
- package/dist/content/document.js +1 -0
- package/dist/content/document.js.map +1 -1
- package/dist/content/index.d.ts +7 -2
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +4 -1
- package/dist/content/index.js.map +1 -1
- package/dist/content/markdown.d.ts.map +1 -1
- package/dist/content/markdown.js +24 -2
- package/dist/content/markdown.js.map +1 -1
- package/dist/content/parser.d.ts.map +1 -1
- package/dist/content/parser.js +11 -4
- package/dist/content/parser.js.map +1 -1
- package/dist/content/save.d.ts +5 -0
- package/dist/content/save.d.ts.map +1 -1
- package/dist/content/save.js +44 -1
- package/dist/content/save.js.map +1 -1
- package/dist/content/schedule.d.ts +125 -0
- package/dist/content/schedule.d.ts.map +1 -0
- package/dist/content/schedule.js +207 -0
- package/dist/content/schedule.js.map +1 -0
- package/dist/content/search.d.ts +64 -0
- package/dist/content/search.d.ts.map +1 -0
- package/dist/content/search.js +136 -0
- package/dist/content/search.js.map +1 -0
- package/dist/content/store.d.ts +220 -7
- package/dist/content/store.d.ts.map +1 -1
- package/dist/content/store.js +392 -67
- package/dist/content/store.js.map +1 -1
- package/dist/content/sync.d.ts +7 -1
- package/dist/content/sync.d.ts.map +1 -1
- package/dist/content/sync.js +18 -10
- package/dist/content/sync.js.map +1 -1
- package/dist/content/time.d.ts +56 -0
- package/dist/content/time.d.ts.map +1 -0
- package/dist/content/time.js +141 -0
- package/dist/content/time.js.map +1 -0
- package/dist/content/writer.d.ts.map +1 -1
- package/dist/content/writer.js +3 -0
- package/dist/content/writer.js.map +1 -1
- package/dist/env.d.ts +70 -5
- package/dist/env.d.ts.map +1 -1
- package/dist/federation/actor.d.ts +95 -41
- package/dist/federation/actor.d.ts.map +1 -1
- package/dist/federation/actor.js +180 -53
- package/dist/federation/actor.js.map +1 -1
- package/dist/federation/article.d.ts +44 -17
- package/dist/federation/article.d.ts.map +1 -1
- package/dist/federation/article.js +98 -46
- package/dist/federation/article.js.map +1 -1
- package/dist/federation/delivery.d.ts +63 -15
- package/dist/federation/delivery.d.ts.map +1 -1
- package/dist/federation/delivery.js +154 -80
- package/dist/federation/delivery.js.map +1 -1
- package/dist/federation/federation.d.ts +22 -17
- package/dist/federation/federation.d.ts.map +1 -1
- package/dist/federation/federation.js +98 -77
- package/dist/federation/federation.js.map +1 -1
- package/dist/federation/followers.d.ts +3 -2
- package/dist/federation/followers.d.ts.map +1 -1
- package/dist/federation/followers.js +6 -5
- package/dist/federation/followers.js.map +1 -1
- package/dist/federation/import-wordpress.d.ts +128 -0
- package/dist/federation/import-wordpress.d.ts.map +1 -0
- package/dist/federation/import-wordpress.js +426 -0
- package/dist/federation/import-wordpress.js.map +1 -0
- package/dist/federation/inbox.d.ts +36 -13
- package/dist/federation/inbox.d.ts.map +1 -1
- package/dist/federation/inbox.js +120 -49
- package/dist/federation/inbox.js.map +1 -1
- package/dist/federation/index.d.ts +22 -10
- package/dist/federation/index.d.ts.map +1 -1
- package/dist/federation/index.js +13 -8
- package/dist/federation/index.js.map +1 -1
- package/dist/federation/keys.d.ts +98 -20
- package/dist/federation/keys.d.ts.map +1 -1
- package/dist/federation/keys.js +269 -39
- package/dist/federation/keys.js.map +1 -1
- package/dist/federation/mount.d.ts +46 -10
- package/dist/federation/mount.d.ts.map +1 -1
- package/dist/federation/mount.js +300 -28
- package/dist/federation/mount.js.map +1 -1
- package/dist/federation/paths.d.ts +23 -35
- package/dist/federation/paths.d.ts.map +1 -1
- package/dist/federation/paths.js +26 -39
- package/dist/federation/paths.js.map +1 -1
- package/dist/federation/records.d.ts +223 -0
- package/dist/federation/records.d.ts.map +1 -0
- package/dist/federation/records.js +506 -0
- package/dist/federation/records.js.map +1 -0
- package/dist/federation/relays.d.ts +114 -0
- package/dist/federation/relays.d.ts.map +1 -0
- package/dist/federation/relays.js +290 -0
- package/dist/federation/relays.js.map +1 -0
- package/dist/federation/replies.d.ts +80 -0
- package/dist/federation/replies.d.ts.map +1 -0
- package/dist/federation/replies.js +166 -0
- package/dist/federation/replies.js.map +1 -0
- package/dist/federation/wordpress.d.ts +131 -0
- package/dist/federation/wordpress.d.ts.map +1 -0
- package/dist/federation/wordpress.js +341 -0
- package/dist/federation/wordpress.js.map +1 -0
- package/dist/files/atomic.d.ts +103 -0
- package/dist/files/atomic.d.ts.map +1 -0
- package/dist/files/atomic.js +150 -0
- package/dist/files/atomic.js.map +1 -0
- package/dist/files/index.d.ts +3 -0
- package/dist/files/index.d.ts.map +1 -0
- package/dist/files/index.js +2 -0
- package/dist/files/index.js.map +1 -0
- package/dist/forms/protection.d.ts +85 -0
- package/dist/forms/protection.d.ts.map +1 -0
- package/dist/forms/protection.js +116 -0
- package/dist/forms/protection.js.map +1 -0
- package/dist/images/icons.d.ts +59 -0
- package/dist/images/icons.d.ts.map +1 -0
- package/dist/images/icons.js +131 -0
- package/dist/images/icons.js.map +1 -0
- package/dist/images/index.d.ts +7 -0
- package/dist/images/index.d.ts.map +1 -0
- package/dist/images/index.js +4 -0
- package/dist/images/index.js.map +1 -0
- package/dist/images/markup.d.ts +47 -0
- package/dist/images/markup.d.ts.map +1 -0
- package/dist/images/markup.js +175 -0
- package/dist/images/markup.js.map +1 -0
- package/dist/images/paths.d.ts +34 -0
- package/dist/images/paths.d.ts.map +1 -0
- package/dist/images/paths.js +41 -0
- package/dist/images/paths.js.map +1 -0
- package/dist/images/variants.d.ts +104 -0
- package/dist/images/variants.d.ts.map +1 -0
- package/dist/images/variants.js +320 -0
- package/dist/images/variants.js.map +1 -0
- package/dist/index.d.ts +123 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +344 -25
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +18 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +34 -3
- package/dist/init.js.map +1 -1
- package/dist/mail/brevo.d.ts +35 -0
- package/dist/mail/brevo.d.ts.map +1 -0
- package/dist/mail/brevo.js +85 -0
- package/dist/mail/brevo.js.map +1 -0
- package/dist/mail/credentials.d.ts +62 -0
- package/dist/mail/credentials.d.ts.map +1 -0
- package/dist/mail/credentials.js +86 -0
- package/dist/mail/credentials.js.map +1 -0
- package/dist/mail/index.d.ts +15 -0
- package/dist/mail/index.d.ts.map +1 -0
- package/dist/mail/index.js +8 -0
- package/dist/mail/index.js.map +1 -0
- package/dist/mail/memory.d.ts +35 -0
- package/dist/mail/memory.d.ts.map +1 -0
- package/dist/mail/memory.js +29 -0
- package/dist/mail/memory.js.map +1 -0
- package/dist/mail/provider.d.ts +80 -0
- package/dist/mail/provider.d.ts.map +1 -0
- package/dist/mail/provider.js +23 -0
- package/dist/mail/provider.js.map +1 -0
- package/dist/mail/service.d.ts +156 -0
- package/dist/mail/service.d.ts.map +1 -0
- package/dist/mail/service.js +265 -0
- package/dist/mail/service.js.map +1 -0
- package/dist/mail/smtp.d.ts +39 -0
- package/dist/mail/smtp.d.ts.map +1 -0
- package/dist/mail/smtp.js +58 -0
- package/dist/mail/smtp.js.map +1 -0
- package/dist/mail/templates.d.ts +91 -0
- package/dist/mail/templates.d.ts.map +1 -0
- package/dist/mail/templates.js +69 -0
- package/dist/mail/templates.js.map +1 -0
- package/dist/notifications/comments.d.ts +66 -0
- package/dist/notifications/comments.d.ts.map +1 -0
- package/dist/notifications/comments.js +151 -0
- package/dist/notifications/comments.js.map +1 -0
- package/dist/notifications/digest.d.ts +135 -0
- package/dist/notifications/digest.d.ts.map +1 -0
- package/dist/notifications/digest.js +287 -0
- package/dist/notifications/digest.js.map +1 -0
- package/dist/notifications/index.d.ts +13 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +8 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/links.d.ts +60 -0
- package/dist/notifications/links.d.ts.map +1 -0
- package/dist/notifications/links.js +63 -0
- package/dist/notifications/links.js.map +1 -0
- package/dist/notifications/optouts.d.ts +48 -0
- package/dist/notifications/optouts.d.ts.map +1 -0
- package/dist/notifications/optouts.js +93 -0
- package/dist/notifications/optouts.js.map +1 -0
- package/dist/notifications/preferences.d.ts +158 -0
- package/dist/notifications/preferences.d.ts.map +1 -0
- package/dist/notifications/preferences.js +195 -0
- package/dist/notifications/preferences.js.map +1 -0
- package/dist/notifications/routes.d.ts +28 -0
- package/dist/notifications/routes.d.ts.map +1 -0
- package/dist/notifications/routes.js +184 -0
- package/dist/notifications/routes.js.map +1 -0
- package/dist/notifications/tokens.d.ts +60 -0
- package/dist/notifications/tokens.d.ts.map +1 -0
- package/dist/notifications/tokens.js +125 -0
- package/dist/notifications/tokens.js.map +1 -0
- package/dist/notify.d.ts +84 -0
- package/dist/notify.d.ts.map +1 -0
- package/dist/notify.js +149 -0
- package/dist/notify.js.map +1 -0
- package/dist/web/archive.d.ts +49 -0
- package/dist/web/archive.d.ts.map +1 -0
- package/dist/web/archive.js +59 -0
- package/dist/web/archive.js.map +1 -0
- package/dist/web/assets.d.ts +6 -5
- package/dist/web/assets.d.ts.map +1 -1
- package/dist/web/assets.js +5 -7
- package/dist/web/assets.js.map +1 -1
- package/dist/web/authors.d.ts +143 -0
- package/dist/web/authors.d.ts.map +1 -0
- package/dist/web/authors.js +178 -0
- package/dist/web/authors.js.map +1 -0
- package/dist/web/comments.d.ts +60 -0
- package/dist/web/comments.d.ts.map +1 -0
- package/dist/web/comments.js +207 -0
- package/dist/web/comments.js.map +1 -0
- package/dist/web/context.d.ts +162 -28
- package/dist/web/context.d.ts.map +1 -1
- package/dist/web/context.js +123 -26
- package/dist/web/context.js.map +1 -1
- package/dist/web/conversation.d.ts +209 -0
- package/dist/web/conversation.d.ts.map +1 -0
- package/dist/web/conversation.js +557 -0
- package/dist/web/conversation.js.map +1 -0
- package/dist/web/documents.d.ts +36 -6
- package/dist/web/documents.d.ts.map +1 -1
- package/dist/web/documents.js +52 -10
- package/dist/web/documents.js.map +1 -1
- package/dist/web/feed-atom.d.ts +22 -0
- package/dist/web/feed-atom.d.ts.map +1 -0
- package/dist/web/feed-atom.js +83 -0
- package/dist/web/feed-atom.js.map +1 -0
- package/dist/web/feed-item.d.ts +145 -0
- package/dist/web/feed-item.d.ts.map +1 -0
- package/dist/web/feed-item.js +120 -0
- package/dist/web/feed-item.js.map +1 -0
- package/dist/web/feed-json.d.ts +79 -0
- package/dist/web/feed-json.d.ts.map +1 -0
- package/dist/web/feed-json.js +53 -0
- package/dist/web/feed-json.js.map +1 -0
- package/dist/web/feed-rss.d.ts +26 -0
- package/dist/web/feed-rss.d.ts.map +1 -0
- package/dist/web/feed-rss.js +203 -0
- package/dist/web/feed-rss.js.map +1 -0
- package/dist/web/feed-source.d.ts +244 -0
- package/dist/web/feed-source.d.ts.map +1 -0
- package/dist/web/feed-source.js +201 -0
- package/dist/web/feed-source.js.map +1 -0
- package/dist/web/feed-xml.d.ts +63 -0
- package/dist/web/feed-xml.d.ts.map +1 -0
- package/dist/web/feed-xml.js +116 -0
- package/dist/web/feed-xml.js.map +1 -0
- package/dist/web/feeds.d.ts +31 -96
- package/dist/web/feeds.d.ts.map +1 -1
- package/dist/web/feeds.js +94 -198
- package/dist/web/feeds.js.map +1 -1
- package/dist/web/health.d.ts +31 -0
- package/dist/web/health.d.ts.map +1 -0
- package/dist/web/health.js +44 -0
- package/dist/web/health.js.map +1 -0
- package/dist/web/index.d.ts +30 -10
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +18 -7
- package/dist/web/index.js.map +1 -1
- package/dist/web/navigation.d.ts +195 -0
- package/dist/web/navigation.d.ts.map +1 -0
- package/dist/web/navigation.js +300 -0
- package/dist/web/navigation.js.map +1 -0
- package/dist/web/negotiate.d.ts +31 -0
- package/dist/web/negotiate.d.ts.map +1 -1
- package/dist/web/negotiate.js +31 -11
- package/dist/web/negotiate.js.map +1 -1
- package/dist/web/recent.d.ts +43 -0
- package/dist/web/recent.d.ts.map +1 -0
- package/dist/web/recent.js +37 -0
- package/dist/web/recent.js.map +1 -0
- package/dist/web/render.d.ts +203 -9
- package/dist/web/render.d.ts.map +1 -1
- package/dist/web/render.js +341 -17
- package/dist/web/render.js.map +1 -1
- package/dist/web/routes.d.ts +25 -8
- package/dist/web/routes.d.ts.map +1 -1
- package/dist/web/routes.js +854 -93
- package/dist/web/routes.js.map +1 -1
- package/dist/web/sanitize.d.ts +19 -0
- package/dist/web/sanitize.d.ts.map +1 -0
- package/dist/web/sanitize.js +272 -0
- package/dist/web/sanitize.js.map +1 -0
- package/dist/web/search.d.ts +79 -0
- package/dist/web/search.d.ts.map +1 -0
- package/dist/web/search.js +94 -0
- package/dist/web/search.js.map +1 -0
- package/dist/web/sitemap.d.ts +120 -0
- package/dist/web/sitemap.d.ts.map +1 -0
- package/dist/web/sitemap.js +199 -0
- package/dist/web/sitemap.js.map +1 -0
- package/dist/web/taxonomy.d.ts +152 -0
- package/dist/web/taxonomy.d.ts.map +1 -0
- package/dist/web/taxonomy.js +237 -0
- package/dist/web/taxonomy.js.map +1 -0
- package/dist/web/templates.d.ts +54 -16
- package/dist/web/templates.d.ts.map +1 -1
- package/dist/web/templates.js +109 -28
- package/dist/web/templates.js.map +1 -1
- package/dist/web/themes.d.ts +226 -0
- package/dist/web/themes.d.ts.map +1 -0
- package/dist/web/themes.js +314 -0
- package/dist/web/themes.js.map +1 -0
- package/dist/webmention/discovery.d.ts +51 -0
- package/dist/webmention/discovery.d.ts.map +1 -0
- package/dist/webmention/discovery.js +176 -0
- package/dist/webmention/discovery.js.map +1 -0
- package/dist/webmention/html.d.ts +71 -0
- package/dist/webmention/html.d.ts.map +1 -0
- package/dist/webmention/html.js +285 -0
- package/dist/webmention/html.js.map +1 -0
- package/dist/webmention/index.d.ts +12 -0
- package/dist/webmention/index.d.ts.map +1 -0
- package/dist/webmention/index.js +8 -0
- package/dist/webmention/index.js.map +1 -0
- package/dist/webmention/links.d.ts +18 -0
- package/dist/webmention/links.d.ts.map +1 -0
- package/dist/webmention/links.js +64 -0
- package/dist/webmention/links.js.map +1 -0
- package/dist/webmention/microformats.d.ts +93 -0
- package/dist/webmention/microformats.d.ts.map +1 -0
- package/dist/webmention/microformats.js +376 -0
- package/dist/webmention/microformats.js.map +1 -0
- package/dist/webmention/receive.d.ts +141 -0
- package/dist/webmention/receive.d.ts.map +1 -0
- package/dist/webmention/receive.js +264 -0
- package/dist/webmention/receive.js.map +1 -0
- package/dist/webmention/routes.d.ts +37 -0
- package/dist/webmention/routes.d.ts.map +1 -0
- package/dist/webmention/routes.js +75 -0
- package/dist/webmention/routes.js.map +1 -0
- package/dist/webmention/service.d.ts +94 -0
- package/dist/webmention/service.d.ts.map +1 -0
- package/dist/webmention/service.js +207 -0
- package/dist/webmention/service.js.map +1 -0
- package/package.json +11 -6
- package/templates/site/content/_data/site.json +8 -0
- package/templates/site/content/pages/about.md +7 -0
- package/templates/site/content/posts/2026-01-01-hello-world.md +5 -3
- package/templates/site/geekity.config.ts +11 -1
- package/templates/site/tsconfig.json +1 -1
- package/themes/default/README.md +1216 -50
- package/themes/default/layouts/404.njk +2 -3
- package/themes/default/layouts/author.njk +36 -0
- package/themes/default/layouts/base.njk +161 -31
- package/themes/default/layouts/category.njk +27 -0
- package/themes/default/layouts/front-page.njk +56 -0
- package/themes/default/layouts/home.njk +17 -1
- package/themes/default/layouts/page.njk +57 -5
- package/themes/default/layouts/post.njk +78 -19
- package/themes/default/layouts/search.njk +57 -0
- package/themes/default/layouts/tag.njk +3 -6
- package/themes/default/mail/comment-digest.html.njk +37 -0
- package/themes/default/mail/comment-digest.subject.njk +3 -0
- package/themes/default/mail/comment-digest.txt.njk +40 -0
- package/themes/default/mail/comment-pending.html.njk +27 -0
- package/themes/default/mail/comment-pending.subject.njk +2 -0
- package/themes/default/mail/comment-pending.txt.njk +24 -0
- package/themes/default/mail/comment-reply.html.njk +21 -0
- package/themes/default/mail/comment-reply.subject.njk +2 -0
- package/themes/default/mail/comment-reply.txt.njk +22 -0
- package/themes/default/mail/contact-message.html.njk +24 -0
- package/themes/default/mail/contact-message.subject.njk +5 -0
- package/themes/default/mail/contact-message.txt.njk +20 -0
- package/themes/default/mail/password-changed.html.njk +18 -0
- package/themes/default/mail/password-changed.subject.njk +2 -0
- package/themes/default/mail/password-changed.txt.njk +17 -0
- package/themes/default/mail/password-reset.html.njk +22 -0
- package/themes/default/mail/password-reset.subject.njk +2 -0
- package/themes/default/mail/password-reset.txt.njk +18 -0
- package/themes/default/mail/test.html.njk +14 -0
- package/themes/default/mail/test.subject.njk +2 -0
- package/themes/default/mail/test.txt.njk +12 -0
- package/themes/default/partials/archive.njk +27 -0
- package/themes/default/partials/bio.njk +71 -0
- package/themes/default/partials/comment-form.njk +137 -0
- package/themes/default/partials/contact-form.njk +88 -0
- package/themes/default/partials/conversation.njk +139 -0
- package/themes/default/partials/feeds.njk +26 -0
- package/themes/default/partials/jsonld.njk +120 -0
- package/themes/default/partials/menu.njk +32 -0
- package/themes/default/partials/pagination.njk +12 -6
- package/themes/default/partials/post-list.njk +51 -18
- package/themes/default/partials/search-form.njk +22 -0
- package/themes/default/partials/tags.njk +26 -5
- package/themes/default/static/highlight.js +4 -0
- package/themes/default/static/style.css +1142 -194
- package/themes/default/theme.json +9 -0
- package/admin/layouts/federation.njk +0 -145
- package/admin/layouts/login.njk +0 -23
- package/admin/layouts/settings.njk +0 -116
- package/admin/layouts/setup.njk +0 -27
- package/admin/layouts/users.njk +0 -91
- /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":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;;;;;;;;;;GAeG;AAEH,8EAA8E;AAC9E,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC;AAEpC,kFAAkF;AAClF,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC;AAUlC;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,KAAK,GAAG,CAAC;IACpD,MAAM,IAAI,GAAG,IAAI,WAAW,IAAI,kBAAkB,CAAC,QAAQ,CAAC,GAAG,CAAC;IAChE,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,YAAY,IAAI,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC;AAC7E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,MAAkB;IACjE,OAAO,aAAa,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAE9C,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;IACzE,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,WAAW;QAAE,OAAO,SAAS,CAAC;IAElD,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC7B,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAEhE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;IAC9D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,YAAY;QAAE,OAAO,SAAS,CAAC;IAE5E,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3E,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAsB,EACtB,MAA0B;IAE1B,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACrC,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAEpC,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC;IAClE,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC;IAEhD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IAC3E,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACnD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,KAAsB,EAAE,IAAU;IAC5D,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC;QACnE,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,EAAE,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC;YAAE,SAAS;QACvF,IAAI,aAAa,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7E,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAsBD;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAAU;IACvC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAEzB,OAAO;QACL,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,IAAI,EAAE,OAAO,EAAE,WAAW,IAAI,IAAI,CAAC,QAAQ;QAC3C,GAAG,EAAE,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC9B,GAAG,CAAC,OAAO,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;QAC3D,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;QACpE,GAAG,CAAC,OAAO,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC1E,GAAG,CAAC,OAAO,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC1E,GAAG,CAAC,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;KAClE,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAsB,EACtB,MAA0B;IAE1B,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC1C,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAsB,EACtB,MAA0B;IAE1B,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACnC,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAElC,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACxC,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;AAC9D,CAAC"}
|
|
@@ -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":"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"}
|