@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,280 @@
|
|
|
1
|
+
import { CSRF_FIELD } from '../admin/session.js';
|
|
2
|
+
import { ADDRESS_SALT_FILE, FORM_LOADED_FIELD, FORM_TRAP_FIELD, formTimingRefusal, trapped, } from '../forms/protection.js';
|
|
3
|
+
import { renderCommentMarkdown } from './markdown.js';
|
|
4
|
+
import { intakeComment } from './records.js';
|
|
5
|
+
/**
|
|
6
|
+
* What happens between somebody pressing Post and a proposed comment.
|
|
7
|
+
*
|
|
8
|
+
* Three defences run before anything is proposed, in the order that costs least
|
|
9
|
+
* and gives away least — the honeypot, the age of the form and the per-address
|
|
10
|
+
* rate limit. The first two are `forms/protection.ts`, shared with the contact
|
|
11
|
+
* form (TASK-56) so the two public forms cannot drift into different rules; the
|
|
12
|
+
* third is the same limiter the login form uses, over the commenter's address,
|
|
13
|
+
* so one machine cannot post a hundred comments while a moderator sleeps.
|
|
14
|
+
*
|
|
15
|
+
* What survives all three is handed to {@link intakeComment}, which is where
|
|
16
|
+
* every comment on this site is written whatever proposed it: the checker, the
|
|
17
|
+
* verdict-to-status rule, the file, the index and the notice all live there, so
|
|
18
|
+
* a form submission and a webmention cannot be treated by two sets of rules.
|
|
19
|
+
*
|
|
20
|
+
* The {@link CommentChecker} interface stays here because this is where it
|
|
21
|
+
* reads: it is the one seam a third-party service plugs into, everything
|
|
22
|
+
* Akismet's API asks for is on the submission it is handed, and its four
|
|
23
|
+
* answers are the four the intake understands. Nothing else in the CMS has to
|
|
24
|
+
* know such a service exists.
|
|
25
|
+
*/
|
|
26
|
+
// Re-exported where they have always been, so a site that imported them from
|
|
27
|
+
// the package, and the modules in here that did, are unaffected by the move.
|
|
28
|
+
export { hashClientAddress, MAXIMUM_FORM_AGE_SECONDS, MINIMUM_SUBMIT_SECONDS, } from '../forms/protection.js';
|
|
29
|
+
/** Where the salt that hides commenters' addresses lives, under `dataDir`. */
|
|
30
|
+
export const COMMENT_SALT_FILE = ADDRESS_SALT_FILE;
|
|
31
|
+
/** The fields the comment form submits. */
|
|
32
|
+
export const COMMENT_FIELDS = {
|
|
33
|
+
/** The post being commented on, as its slug. */
|
|
34
|
+
post: 'post',
|
|
35
|
+
/** The commenter's name. Required. */
|
|
36
|
+
name: 'name',
|
|
37
|
+
/** Their email. Optional, and never shown. */
|
|
38
|
+
email: 'email',
|
|
39
|
+
/** Their website. Optional. */
|
|
40
|
+
url: 'url',
|
|
41
|
+
/** The comment itself, as Markdown. Required. */
|
|
42
|
+
body: 'body',
|
|
43
|
+
/** The comment being answered, or empty for one answering the post. */
|
|
44
|
+
inReplyTo: 'in_reply_to',
|
|
45
|
+
/** The honeypot, which every public form here spells the same way. */
|
|
46
|
+
trap: FORM_TRAP_FIELD,
|
|
47
|
+
/** When the form was rendered, in epoch milliseconds. */
|
|
48
|
+
loaded: FORM_LOADED_FIELD,
|
|
49
|
+
/**
|
|
50
|
+
* The CSRF token, which only the signed-in form carries (TASK-103).
|
|
51
|
+
*
|
|
52
|
+
* Spelled exactly as every admin form spells it, because it is the same
|
|
53
|
+
* per-session token and is checked by the same comparison.
|
|
54
|
+
*/
|
|
55
|
+
csrf: CSRF_FIELD,
|
|
56
|
+
/**
|
|
57
|
+
* "Tell me when somebody answers this."
|
|
58
|
+
*
|
|
59
|
+
* A checkbox, so any non-empty value is a yes. It only ever means anything
|
|
60
|
+
* alongside an email address, and the form only offers it when the site can
|
|
61
|
+
* actually send mail (TASK-55).
|
|
62
|
+
*/
|
|
63
|
+
notify: 'notify',
|
|
64
|
+
};
|
|
65
|
+
/** How many comments one address may post before it has to wait. */
|
|
66
|
+
export const COMMENT_RATE_LIMIT = 5;
|
|
67
|
+
/** How long that window is, and how long the first wait after it lasts. */
|
|
68
|
+
export const COMMENT_RATE_WINDOW_SECONDS = 600;
|
|
69
|
+
/** The longest a name may be. */
|
|
70
|
+
export const MAXIMUM_NAME_LENGTH = 80;
|
|
71
|
+
/** The longest an email or a website may be. */
|
|
72
|
+
export const MAXIMUM_URL_LENGTH = 500;
|
|
73
|
+
/** The longest a comment may be, in characters. */
|
|
74
|
+
export const MAXIMUM_BODY_LENGTH = 10_000;
|
|
75
|
+
/**
|
|
76
|
+
* What is wrong with a submitted comment, as a person would be told it.
|
|
77
|
+
*
|
|
78
|
+
* Only the things a person can fix. The honeypot, the form's age and the rate
|
|
79
|
+
* limit are not here: none of them is the commenter's mistake, and two of them
|
|
80
|
+
* should not be explained to whoever tripped them.
|
|
81
|
+
*
|
|
82
|
+
* A signed-in commenter is asked for nothing but the comment (TASK-103), so
|
|
83
|
+
* only the comment is checked: the name, the email and the website come off
|
|
84
|
+
* the account and there are no boxes on the form to put anything right in.
|
|
85
|
+
*/
|
|
86
|
+
export function commentProblems(form, signedIn = false) {
|
|
87
|
+
const problems = {};
|
|
88
|
+
const body = form.body.trim();
|
|
89
|
+
if (body === '')
|
|
90
|
+
problems.body = 'A comment needs something in it.';
|
|
91
|
+
else if (body.length > MAXIMUM_BODY_LENGTH) {
|
|
92
|
+
problems.body = `That comment is longer than ${String(MAXIMUM_BODY_LENGTH)} characters.`;
|
|
93
|
+
}
|
|
94
|
+
if (signedIn)
|
|
95
|
+
return problems;
|
|
96
|
+
const name = form.name.trim();
|
|
97
|
+
if (name === '')
|
|
98
|
+
problems.name = 'A comment needs a name to go under.';
|
|
99
|
+
else if (name.length > MAXIMUM_NAME_LENGTH) {
|
|
100
|
+
problems.name = `That name is longer than ${String(MAXIMUM_NAME_LENGTH)} characters.`;
|
|
101
|
+
}
|
|
102
|
+
const email = form.email.trim();
|
|
103
|
+
if (email !== '' && !isEmail(email)) {
|
|
104
|
+
problems.email =
|
|
105
|
+
'That does not look like an email address. Leave it empty if you would rather.';
|
|
106
|
+
}
|
|
107
|
+
const url = form.url.trim();
|
|
108
|
+
if (url !== '' && normalizeWebsite(url) === undefined) {
|
|
109
|
+
problems.url = 'A website is an http:// or https:// address.';
|
|
110
|
+
}
|
|
111
|
+
return problems;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Take a submitted form and, if it survives everything, store the comment.
|
|
115
|
+
*
|
|
116
|
+
* The caller has already decided that the post is open: whether a form should
|
|
117
|
+
* exist at all is {@link commentsOpen}'s question, and asking it here as well
|
|
118
|
+
* would let the two answers differ.
|
|
119
|
+
*/
|
|
120
|
+
export async function submitComment(options) {
|
|
121
|
+
const { records, document, form, throttle } = options;
|
|
122
|
+
const now = options.now ?? new Date();
|
|
123
|
+
// Whether the site knows who this is. It decides the attribution, the two
|
|
124
|
+
// cheap defences and where the comment lands, so it is read once.
|
|
125
|
+
const signedIn = options.author;
|
|
126
|
+
// The honeypot first, because it costs one comparison and because a
|
|
127
|
+
// submission that tripped it should not reach anything that could tell it so.
|
|
128
|
+
// Not for a signed-in commenter: their form carries no honeypot, and a
|
|
129
|
+
// session is better evidence than an empty box.
|
|
130
|
+
if (signedIn === undefined && trapped(form.trap))
|
|
131
|
+
return refused({ kind: 'discarded' });
|
|
132
|
+
const problems = commentProblems(form, signedIn !== undefined);
|
|
133
|
+
if (Object.keys(problems).length > 0)
|
|
134
|
+
return refused({ kind: 'invalid', problems });
|
|
135
|
+
// The form's age, for the same reason and with the same exception: a
|
|
136
|
+
// signed-in form carries no loaded-at stamp to measure.
|
|
137
|
+
if (signedIn === undefined) {
|
|
138
|
+
const timing = formTimingRefusal(form.loaded, now);
|
|
139
|
+
if (timing !== undefined)
|
|
140
|
+
return refused({ kind: timing });
|
|
141
|
+
}
|
|
142
|
+
// The rate limit stays either way. It is not about proving somebody is a
|
|
143
|
+
// person; it is about what one address may do in ten minutes, and a stolen
|
|
144
|
+
// session is exactly the case it would be wanted for.
|
|
145
|
+
const keys = commentKeys(options.address);
|
|
146
|
+
const wait = throttle.retryAfter(keys);
|
|
147
|
+
if (wait !== undefined)
|
|
148
|
+
return refused({ kind: 'rate-limited', retryAfter: wait });
|
|
149
|
+
const markdown = form.body.trim();
|
|
150
|
+
const author = signedIn === undefined
|
|
151
|
+
? {
|
|
152
|
+
name: form.name.trim(),
|
|
153
|
+
url: normalizeWebsite(form.url.trim()) ?? null,
|
|
154
|
+
email: form.email.trim() === '' ? null : form.email.trim(),
|
|
155
|
+
// A form asks for no picture: only a webmention brings one (TASK-51).
|
|
156
|
+
avatar: null,
|
|
157
|
+
}
|
|
158
|
+
: {
|
|
159
|
+
name: signedIn.name,
|
|
160
|
+
url: signedIn.url,
|
|
161
|
+
email: signedIn.email,
|
|
162
|
+
avatar: null,
|
|
163
|
+
};
|
|
164
|
+
const proposed = {
|
|
165
|
+
slug: document.slug,
|
|
166
|
+
permalink: document.permalink,
|
|
167
|
+
source: 'comment',
|
|
168
|
+
kind: 'reply',
|
|
169
|
+
author,
|
|
170
|
+
content: { markdown, html: renderCommentMarkdown(markdown) },
|
|
171
|
+
submitted: now.toISOString(),
|
|
172
|
+
inReplyTo: parentOf(form.inReplyTo, document.slug, records),
|
|
173
|
+
// A comment written here lives here: only a webmention has a page of its
|
|
174
|
+
// own somewhere else (TASK-51).
|
|
175
|
+
url: null,
|
|
176
|
+
// Only ever true when there is an address to send to. A box ticked with
|
|
177
|
+
// the email field empty is somebody asking to be told at nowhere.
|
|
178
|
+
notify: options.notifiable === true && form.notify.trim() !== '' && author.email !== null,
|
|
179
|
+
};
|
|
180
|
+
const outcome = await intakeComment({
|
|
181
|
+
records,
|
|
182
|
+
// Geekity has one role, so anybody who can sign in can already publish a
|
|
183
|
+
// post and approve any comment. A comment of theirs waiting in a queue for
|
|
184
|
+
// them to approve would be theatre, and a spam service has no say in what
|
|
185
|
+
// the owner of the site says — so a signed-in comment arrives at the
|
|
186
|
+
// intake as exactly what it is, the site's own author writing, and is
|
|
187
|
+
// approved on arrival with no call to the checker (TASK-103).
|
|
188
|
+
origin: signedIn === undefined ? 'form' : 'moderator',
|
|
189
|
+
comment: proposed,
|
|
190
|
+
post: {
|
|
191
|
+
title: document.title,
|
|
192
|
+
url: absolute(document.permalink, options.baseUrl),
|
|
193
|
+
},
|
|
194
|
+
dataDir: options.dataDir,
|
|
195
|
+
baseUrl: options.baseUrl,
|
|
196
|
+
checker: options.checker,
|
|
197
|
+
notices: options.notices,
|
|
198
|
+
address: options.address,
|
|
199
|
+
userAgent: options.userAgent,
|
|
200
|
+
referrer: options.referrer,
|
|
201
|
+
});
|
|
202
|
+
// Counted against the address whatever became of it: a machine posting
|
|
203
|
+
// rubbish should not get unlimited free attempts because the rubbish was
|
|
204
|
+
// recognised.
|
|
205
|
+
throttle.fail(keys);
|
|
206
|
+
return outcome.kind === 'stored'
|
|
207
|
+
? { kind: 'stored', comment: outcome.comment }
|
|
208
|
+
: refused({ kind: 'discarded' });
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The comment a submission answers, or `null`.
|
|
212
|
+
*
|
|
213
|
+
* A named parent that is not a comment on this very post is dropped rather
|
|
214
|
+
* than stored: threading a reply under something on another page would put it
|
|
215
|
+
* somewhere nobody can see it, and an id somebody made up is not a parent.
|
|
216
|
+
*/
|
|
217
|
+
function parentOf(submitted, slug, records) {
|
|
218
|
+
const id = submitted.trim();
|
|
219
|
+
if (id === '')
|
|
220
|
+
return null;
|
|
221
|
+
const parent = records.admin.getComment(id);
|
|
222
|
+
return parent !== undefined && parent.slug === slug ? parent.id : null;
|
|
223
|
+
}
|
|
224
|
+
/** The keys a submission is rate limited against. */
|
|
225
|
+
export function commentKeys(address) {
|
|
226
|
+
return address === undefined ? [] : [`comment:${address}`];
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* A submitted website as the absolute URL it means, or `undefined` when it
|
|
230
|
+
* means none.
|
|
231
|
+
*
|
|
232
|
+
* A bare `example.com` becomes `https://example.com/`, because that is what
|
|
233
|
+
* somebody typing it meant and refusing it would be pedantry; anything whose
|
|
234
|
+
* scheme is not http or https is refused, because it is going on a page as a
|
|
235
|
+
* link.
|
|
236
|
+
*/
|
|
237
|
+
export function normalizeWebsite(value) {
|
|
238
|
+
const trimmed = value.trim();
|
|
239
|
+
if (trimmed === '' || trimmed.length > MAXIMUM_URL_LENGTH)
|
|
240
|
+
return undefined;
|
|
241
|
+
const candidate = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
242
|
+
let url;
|
|
243
|
+
try {
|
|
244
|
+
url = new URL(candidate);
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:')
|
|
250
|
+
return undefined;
|
|
251
|
+
if (url.hostname === '')
|
|
252
|
+
return undefined;
|
|
253
|
+
return url.href;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Whether something looks like an email address.
|
|
257
|
+
*
|
|
258
|
+
* Deliberately loose. The address is never shown and nothing is ever sent to
|
|
259
|
+
* it (TASK-50 has no email at all), so the only thing checking it buys is
|
|
260
|
+
* catching a typo; a stricter rule would refuse valid addresses and buy
|
|
261
|
+
* nothing.
|
|
262
|
+
*/
|
|
263
|
+
function isEmail(value) {
|
|
264
|
+
if (value.length > MAXIMUM_URL_LENGTH)
|
|
265
|
+
return false;
|
|
266
|
+
return /^[^\s@]+@[^\s@.]+(\.[^\s@.]+)+$/.test(value);
|
|
267
|
+
}
|
|
268
|
+
/** A site-root path as an absolute URL. */
|
|
269
|
+
function absolute(pathname, baseUrl) {
|
|
270
|
+
try {
|
|
271
|
+
return new URL(pathname, baseUrl).href;
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return pathname;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function refused(refusal) {
|
|
278
|
+
return { kind: 'refused', refusal };
|
|
279
|
+
}
|
|
280
|
+
//# sourceMappingURL=submission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"submission.js","sourceRoot":"","sources":["../../src/comments/submission.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
import type { GeekityEnv } from '../env.ts';
|
|
3
|
+
import type { CommentViewer } from './form.ts';
|
|
4
|
+
/**
|
|
5
|
+
* Who the public site knows is reading it, when the request carries a login.
|
|
6
|
+
*
|
|
7
|
+
* The one place outside `/admin` that turns a session cookie into a person.
|
|
8
|
+
* Everything a comment needs about them is answered here — the name it goes
|
|
9
|
+
* under, the archive it links to, the email it is stored with and the token
|
|
10
|
+
* the form has to carry — so the page that draws the form and the endpoint
|
|
11
|
+
* that takes the submission are looking at one reading of one session.
|
|
12
|
+
*
|
|
13
|
+
* Deliberately total about the ways a cookie can be worth nothing. A forged or
|
|
14
|
+
* expired id, a session belonging to a user who has since been deleted, and
|
|
15
|
+
* the anonymous session that holds a CSRF token before anybody has logged in
|
|
16
|
+
* all answer `undefined`, which is the stranger's form: the only thing that
|
|
17
|
+
* gets the short form is a live session naming a user `users.json` still
|
|
18
|
+
* holds, which is the same test the admin guard applies at its own door.
|
|
19
|
+
*/
|
|
20
|
+
export declare function signedInCommenter(c: Context<GeekityEnv>): CommentViewer | undefined;
|
|
21
|
+
//# sourceMappingURL=viewer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewer.d.ts","sourceRoot":"","sources":["../../src/comments/viewer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAIpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,aAAa,GAAG,SAAS,CAwBnF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { findUserById } from '../admin/accounts.js';
|
|
2
|
+
import { sessionIdFrom } from '../admin/session.js';
|
|
3
|
+
import { authorHref } from '../web/authors.js';
|
|
4
|
+
/**
|
|
5
|
+
* Who the public site knows is reading it, when the request carries a login.
|
|
6
|
+
*
|
|
7
|
+
* The one place outside `/admin` that turns a session cookie into a person.
|
|
8
|
+
* Everything a comment needs about them is answered here — the name it goes
|
|
9
|
+
* under, the archive it links to, the email it is stored with and the token
|
|
10
|
+
* the form has to carry — so the page that draws the form and the endpoint
|
|
11
|
+
* that takes the submission are looking at one reading of one session.
|
|
12
|
+
*
|
|
13
|
+
* Deliberately total about the ways a cookie can be worth nothing. A forged or
|
|
14
|
+
* expired id, a session belonging to a user who has since been deleted, and
|
|
15
|
+
* the anonymous session that holds a CSRF token before anybody has logged in
|
|
16
|
+
* all answer `undefined`, which is the stranger's form: the only thing that
|
|
17
|
+
* gets the short form is a live session naming a user `users.json` still
|
|
18
|
+
* holds, which is the same test the admin guard applies at its own door.
|
|
19
|
+
*/
|
|
20
|
+
export function signedInCommenter(c) {
|
|
21
|
+
const id = sessionIdFrom(c);
|
|
22
|
+
if (id === undefined)
|
|
23
|
+
return undefined;
|
|
24
|
+
// Reading a session prunes an expired one, so this is a live session or
|
|
25
|
+
// nothing at all. Measured against the wall clock rather than the site's
|
|
26
|
+
// configured one, because that is the clock a session was minted against and
|
|
27
|
+
// the one the admin guard reads: a login cannot last longer on the public
|
|
28
|
+
// site than it does in the admin.
|
|
29
|
+
const session = c.var.admin.getSession(id);
|
|
30
|
+
if (session?.userId == null)
|
|
31
|
+
return undefined;
|
|
32
|
+
const user = findUserById(c.var.config.dataDir, session.userId);
|
|
33
|
+
if (user === undefined)
|
|
34
|
+
return undefined;
|
|
35
|
+
return {
|
|
36
|
+
// What a byline would print for them, on the same rule: a user who has
|
|
37
|
+
// written no display name is called by their username, which is what
|
|
38
|
+
// their archive is under anyway.
|
|
39
|
+
name: user.profile?.displayName ?? user.username,
|
|
40
|
+
url: authorHref(user.username),
|
|
41
|
+
email: user.email ?? null,
|
|
42
|
+
csrfToken: session.csrfToken,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=viewer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewer.js","sourceRoot":"","sources":["../../src/comments/viewer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG/C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,iBAAiB,CAAC,CAAsB;IACtD,MAAM,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;IAC5B,IAAI,EAAE,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAEvC,wEAAwE;IACxE,yEAAyE;IACzE,6EAA6E;IAC7E,0EAA0E;IAC1E,kCAAkC;IAClC,MAAM,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC3C,IAAI,OAAO,EAAE,MAAM,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC;IAE9C,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAChE,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAEzC,OAAO;QACL,uEAAuE;QACvE,qEAAqE;QACrE,iCAAiC;QACjC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,IAAI,IAAI,CAAC,QAAQ;QAChD,GAAG,EAAE,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC9B,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI;QACzB,SAAS,EAAE,OAAO,CAAC,SAAS;KAC7B,CAAC;AACJ,CAAC"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { KvStore, MessageQueue } from '@fedify/fedify';
|
|
2
|
+
import type { AccessLogWriter } from './access-log.ts';
|
|
3
|
+
import type { CommentChecker } from './comments/submission.ts';
|
|
4
|
+
import type { Clock } from './content/store.ts';
|
|
2
5
|
import type { DocumentChange } from './content/sync.ts';
|
|
6
|
+
import type { MailProvider } from './mail/provider.ts';
|
|
7
|
+
import type { MailLogger } from './mail/service.ts';
|
|
3
8
|
/**
|
|
4
9
|
* Something a site wants to happen when the index changes.
|
|
5
10
|
*
|
|
@@ -44,6 +49,36 @@ export interface FederationOverrides {
|
|
|
44
49
|
*/
|
|
45
50
|
allowPrivateAddress?: boolean | undefined;
|
|
46
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* The mail pieces a site may swap (TASK-53).
|
|
54
|
+
*
|
|
55
|
+
* Everything here has a default that is right for a personal blog: the
|
|
56
|
+
* provider comes from the settings screen and `data/mail.json`, a refused
|
|
57
|
+
* message is tried three times with a growing wait, and attempts go to the
|
|
58
|
+
* console. A site names one of these when it has outgrown that, and a test
|
|
59
|
+
* names them to read back the mail and to not wait for the backoff.
|
|
60
|
+
*/
|
|
61
|
+
export interface MailOverrides {
|
|
62
|
+
/**
|
|
63
|
+
* What carries the site's email.
|
|
64
|
+
*
|
|
65
|
+
* Naming one wins outright over the provider the settings screen chose and
|
|
66
|
+
* the credential in `data/mail.json`, exactly as
|
|
67
|
+
* {@link GeekityConfig.commentChecker} wins over the Akismet key: a site
|
|
68
|
+
* that wrote a provider meant it. `createMemoryMailProvider()` is what a
|
|
69
|
+
* test names here to read back the mail a feature would have sent.
|
|
70
|
+
*/
|
|
71
|
+
provider?: MailProvider | undefined;
|
|
72
|
+
/** How many times one message is tried. Defaults to three. */
|
|
73
|
+
attempts?: number | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* How long to wait before attempt `n + 1`, in milliseconds. Defaults to two
|
|
76
|
+
* seconds, then eight, then eighteen. A test hands in `() => 0`.
|
|
77
|
+
*/
|
|
78
|
+
backoffMs?: ((attempt: number) => number) | undefined;
|
|
79
|
+
/** Where attempts are logged. Defaults to `console`. */
|
|
80
|
+
logger?: MailLogger | undefined;
|
|
81
|
+
}
|
|
47
82
|
/**
|
|
48
83
|
* What a site writes in `geekity.config.ts`. Every field is optional; see
|
|
49
84
|
* {@link DEFAULT_PORT} and friends for what each one falls back to.
|
|
@@ -55,8 +90,17 @@ export interface GeekityConfig {
|
|
|
55
90
|
contentDir?: string;
|
|
56
91
|
/** Directory for derived state such as the SQLite index. Default `<cwd>/data`. Overridden by `GEEKITY_DATA_DIR`. */
|
|
57
92
|
dataDir?: string;
|
|
58
|
-
/**
|
|
59
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Where this site's themes are, one directory per theme, each with a
|
|
95
|
+
* `theme.json` (decision-15). Default `<cwd>/themes`. Overridden by
|
|
96
|
+
* `GEEKITY_THEMES_DIR`. It need not exist: a site using the packaged theme
|
|
97
|
+
* has no themes of its own.
|
|
98
|
+
*
|
|
99
|
+
* Which one is in use is a setting rather than a deployment fact — `theme`
|
|
100
|
+
* in `content/_data/site.json`, chosen on the Appearance screen — because it
|
|
101
|
+
* is a look a site changes, not a path a host decides.
|
|
102
|
+
*/
|
|
103
|
+
themesDir?: string;
|
|
60
104
|
/** Public origin, used for canonical URLs, feeds and ActivityPub ids. Default `http://localhost:<port>`. Overridden by `GEEKITY_BASE_URL`. */
|
|
61
105
|
baseUrl?: string;
|
|
62
106
|
/**
|
|
@@ -84,6 +128,101 @@ export interface GeekityConfig {
|
|
|
84
128
|
* `KNOWN_UPLOAD_TYPES`.
|
|
85
129
|
*/
|
|
86
130
|
uploadTypes?: string[];
|
|
131
|
+
/**
|
|
132
|
+
* Derive smaller and more modern copies of an uploaded raster image, and
|
|
133
|
+
* serve the site's pages a `<picture>` that offers them. Default `true`.
|
|
134
|
+
* Overridden by `GEEKITY_IMAGE_OPTIMIZATION`.
|
|
135
|
+
*
|
|
136
|
+
* Turning it off changes nothing on disk under `content/uploads/` — the
|
|
137
|
+
* original was always the only source of truth (decision-10) — and nothing
|
|
138
|
+
* about the feeds. The pages go back to the plain `<img>` the Markdown
|
|
139
|
+
* asked for, and no encoding happens on upload.
|
|
140
|
+
*/
|
|
141
|
+
imageOptimization?: boolean;
|
|
142
|
+
/**
|
|
143
|
+
* The widths variants are made at, in pixels. Default 320, 640, 960, 1280
|
|
144
|
+
* and 1920, which is 11ty/image's own set. Overridden by
|
|
145
|
+
* `GEEKITY_IMAGE_WIDTHS`, a comma-separated list.
|
|
146
|
+
*
|
|
147
|
+
* Nothing is ever upscaled: a width wider than the original is skipped, and
|
|
148
|
+
* the original's own width is always added, so the `srcset` has a full-size
|
|
149
|
+
* entry whatever the list says.
|
|
150
|
+
*/
|
|
151
|
+
imageWidths?: number[];
|
|
152
|
+
/**
|
|
153
|
+
* The formats variants are made in, besides the original's own. Default
|
|
154
|
+
* `['webp']`. Overridden by `GEEKITY_IMAGE_FORMATS`, a comma-separated list.
|
|
155
|
+
*
|
|
156
|
+
* The original's format is always generated too, because it is what the
|
|
157
|
+
* `<img>` inside the `<picture>` falls back to; these are the `<source>`
|
|
158
|
+
* elements in front of it, in the order they are given. AVIF is opt-in —
|
|
159
|
+
* `['avif', 'webp']` — because its encoder is an order of magnitude slower
|
|
160
|
+
* than WebP's and the encoding happens in the request that uploads the file.
|
|
161
|
+
*/
|
|
162
|
+
imageFormats?: string[];
|
|
163
|
+
/**
|
|
164
|
+
* How many failed sign-ins a username or a client address may make before
|
|
165
|
+
* the admin locks it out. Default 5. Overridden by `GEEKITY_LOGIN_ATTEMPTS`.
|
|
166
|
+
*/
|
|
167
|
+
loginAttempts?: number;
|
|
168
|
+
/**
|
|
169
|
+
* How long the first lockout lasts, in seconds. Default 15 minutes. Each
|
|
170
|
+
* further failure doubles the wait, up to sixteen times the first one.
|
|
171
|
+
* Overridden by `GEEKITY_LOGIN_LOCKOUT`.
|
|
172
|
+
*/
|
|
173
|
+
loginLockout?: number;
|
|
174
|
+
/**
|
|
175
|
+
* Whether an `X-Forwarded-For` header may be believed when the admin decides
|
|
176
|
+
* which client address a failed login belongs to. Default `false`.
|
|
177
|
+
* Overridden by `GEEKITY_TRUST_PROXY`.
|
|
178
|
+
*
|
|
179
|
+
* Off by default because anyone may send that header: believing it on a site
|
|
180
|
+
* reached directly would let an attacker put every guess on a different
|
|
181
|
+
* make-believe address and never be locked out at all. Turn it on only when
|
|
182
|
+
* a reverse proxy in front of the site sets it.
|
|
183
|
+
*/
|
|
184
|
+
trustProxy?: boolean;
|
|
185
|
+
/**
|
|
186
|
+
* Write one line per request — method, path, status, duration — to stdout.
|
|
187
|
+
* Overridden by `GEEKITY_ACCESS_LOG`.
|
|
188
|
+
*
|
|
189
|
+
* Default `false`, which is what `createCms` gets: a CMS embedded in
|
|
190
|
+
* somebody else's server has no business writing to their stdout uninvited.
|
|
191
|
+
* `geekity serve` turns it on, because a server that answers the internet
|
|
192
|
+
* should be able to say what it answered; a config that names this either
|
|
193
|
+
* way is obeyed, and the environment variable wins over both.
|
|
194
|
+
*/
|
|
195
|
+
accessLog?: boolean;
|
|
196
|
+
/**
|
|
197
|
+
* Put the client address on each access-log line. Default `false`.
|
|
198
|
+
* Overridden by `GEEKITY_ACCESS_LOG_ADDRESS`.
|
|
199
|
+
*
|
|
200
|
+
* Off unless the site asks, because an address is personal data and a
|
|
201
|
+
* self-hosted blog has no retention policy for it. Which address is right is
|
|
202
|
+
* {@link GeekityConfig.trustProxy}'s answer, the same one a failed sign-in
|
|
203
|
+
* is counted against.
|
|
204
|
+
*/
|
|
205
|
+
accessLogAddress?: boolean;
|
|
206
|
+
/**
|
|
207
|
+
* Where the access log's lines go. Defaults to stdout, written one line at a
|
|
208
|
+
* time exactly as the CLI writes.
|
|
209
|
+
*
|
|
210
|
+
* Config only, with no environment override: a sink is a function, and the
|
|
211
|
+
* only callers with one are a test reading the lines back and a site that
|
|
212
|
+
* has somewhere of its own to put them.
|
|
213
|
+
*/
|
|
214
|
+
accessLogWriter?: AccessLogWriter;
|
|
215
|
+
/**
|
|
216
|
+
* Fill a missing or empty content directory with the starter site
|
|
217
|
+
* `geekity init` writes, when `geekity serve` starts. Default `false`.
|
|
218
|
+
* Overridden by `GEEKITY_SEED_CONTENT`.
|
|
219
|
+
*
|
|
220
|
+
* Off by default so a site run from npm never has content written that it
|
|
221
|
+
* did not ask for. The Docker image turns it on, because its content volume
|
|
222
|
+
* is empty on a new box. A directory with anything in it at all, even a lone
|
|
223
|
+
* dotfile, is never touched.
|
|
224
|
+
*/
|
|
225
|
+
seedContent?: boolean;
|
|
87
226
|
/**
|
|
88
227
|
* Called for every `created`, `updated` and `deleted` the index records.
|
|
89
228
|
*
|
|
@@ -98,6 +237,37 @@ export interface GeekityConfig {
|
|
|
98
237
|
* same `origin` caveat as {@link GeekityConfig.onDocumentChange}.
|
|
99
238
|
*/
|
|
100
239
|
onPublish?: DocumentChangeHook;
|
|
240
|
+
/**
|
|
241
|
+
* A third-party opinion on native comments: Akismet, or anything else that
|
|
242
|
+
* answers the same three questions (TASK-50).
|
|
243
|
+
*
|
|
244
|
+
* The whole of the CMS's knowledge of such a service. It is asked about
|
|
245
|
+
* every comment that gets past the honeypot, the form's age and the rate
|
|
246
|
+
* limit, and its `spam`, `discard` and `ham` are the three things it can
|
|
247
|
+
* say; a checker that throws is treated as having no opinion, so a service
|
|
248
|
+
* that is down never stops a site taking comments. The moderation screen
|
|
249
|
+
* tells it when a human disagrees.
|
|
250
|
+
*
|
|
251
|
+
* Naming one here wins outright over the Akismet checker the package ships
|
|
252
|
+
* (TASK-52): `createCms` builds that only when this is not set, so a site
|
|
253
|
+
* that wrote a checker gets the checker it wrote whether or not there is a
|
|
254
|
+
* key in `data/akismet.json`.
|
|
255
|
+
*/
|
|
256
|
+
commentChecker?: CommentChecker;
|
|
257
|
+
/**
|
|
258
|
+
* How the site sends email (TASK-53). Every field has a default; see
|
|
259
|
+
* {@link MailOverrides}.
|
|
260
|
+
*/
|
|
261
|
+
mail?: MailOverrides;
|
|
262
|
+
/**
|
|
263
|
+
* What the CMS reads the time from.
|
|
264
|
+
*
|
|
265
|
+
* A post's date decides whether it is public yet (TASK-44), so the index and
|
|
266
|
+
* the scheduler both need a clock, and it is one clock so they cannot
|
|
267
|
+
* disagree. Defaults to {@link systemClock}. A site has no reason to name
|
|
268
|
+
* one; a test that wants to be somewhere else in time does.
|
|
269
|
+
*/
|
|
270
|
+
now?: Clock;
|
|
101
271
|
/**
|
|
102
272
|
* Federation stores and guards. Every field has a default; see
|
|
103
273
|
* {@link FederationOverrides}.
|
|
@@ -114,7 +284,7 @@ export interface ResolvedConfig {
|
|
|
114
284
|
port: number;
|
|
115
285
|
contentDir: string;
|
|
116
286
|
dataDir: string;
|
|
117
|
-
|
|
287
|
+
themesDir: string;
|
|
118
288
|
baseUrl: string;
|
|
119
289
|
/**
|
|
120
290
|
* Where {@link ResolvedConfig.baseUrl} came from.
|
|
@@ -122,7 +292,7 @@ export interface ResolvedConfig {
|
|
|
122
292
|
* `default` means nobody named one and it is the `http://localhost:<port>`
|
|
123
293
|
* fallback, which is what lets the admin's settings screen offer a base URL
|
|
124
294
|
* of its own: an `environment` or `config` value is a deployment fact and
|
|
125
|
-
* wins over
|
|
295
|
+
* wins over the `url` in `content/_data/site.json`.
|
|
126
296
|
*/
|
|
127
297
|
baseUrlSource: 'environment' | 'config' | 'default';
|
|
128
298
|
watch: boolean;
|
|
@@ -130,8 +300,34 @@ export interface ResolvedConfig {
|
|
|
130
300
|
uploadMaxBytes: number;
|
|
131
301
|
/** Normalised: lower case, each with its leading dot. */
|
|
132
302
|
uploadTypes: string[];
|
|
303
|
+
imageOptimization: boolean;
|
|
304
|
+
/** Ascending and without repeats. */
|
|
305
|
+
imageWidths: number[];
|
|
306
|
+
/** Normalised: lower case, in the order the `<source>` elements go. */
|
|
307
|
+
imageFormats: string[];
|
|
308
|
+
loginAttempts: number;
|
|
309
|
+
loginLockout: number;
|
|
310
|
+
trustProxy: boolean;
|
|
311
|
+
accessLog: boolean;
|
|
312
|
+
accessLogAddress: boolean;
|
|
313
|
+
/** The sink, when the site named one; otherwise the log writes to stdout. */
|
|
314
|
+
accessLogWriter: AccessLogWriter | undefined;
|
|
315
|
+
seedContent: boolean;
|
|
133
316
|
onDocumentChange: DocumentChangeHook | undefined;
|
|
134
317
|
onPublish: DocumentChangeHook | undefined;
|
|
318
|
+
/**
|
|
319
|
+
* The comment checker, when the site named one.
|
|
320
|
+
*
|
|
321
|
+
* `undefined` as this leaves {@link resolveConfig}. `createCms` fills it in
|
|
322
|
+
* with the Akismet checker (TASK-52) when the site named none, so by the
|
|
323
|
+
* time a request reads it off the context there is one — and with no key in
|
|
324
|
+
* `data/akismet.json` that one sends nothing anywhere.
|
|
325
|
+
*/
|
|
326
|
+
commentChecker: CommentChecker | undefined;
|
|
327
|
+
/** Mail overrides, empty when the site named none. */
|
|
328
|
+
mail: MailOverrides;
|
|
329
|
+
/** The clock the index and the scheduler read. */
|
|
330
|
+
now: Clock;
|
|
135
331
|
/** Federation stores and guards, empty when the site named none. */
|
|
136
332
|
federation: FederationOverrides;
|
|
137
333
|
}
|
|
@@ -145,7 +341,7 @@ export interface ResolveConfigContext {
|
|
|
145
341
|
export declare const DEFAULT_PORT = 3000;
|
|
146
342
|
export declare const DEFAULT_CONTENT_DIR = "content";
|
|
147
343
|
export declare const DEFAULT_DATA_DIR = "data";
|
|
148
|
-
export declare const
|
|
344
|
+
export declare const DEFAULT_THEMES_DIR = "themes";
|
|
149
345
|
/** How long an admin login lasts by default: fourteen days, in seconds. */
|
|
150
346
|
export declare const DEFAULT_SESSION_LIFETIME: number;
|
|
151
347
|
/** Largest upload a site accepts by default: ten mebibytes. */
|
|
@@ -157,6 +353,30 @@ export declare const DEFAULT_UPLOAD_MAX_BYTES: number;
|
|
|
157
353
|
* and so is opt-in.
|
|
158
354
|
*/
|
|
159
355
|
export declare const DEFAULT_UPLOAD_TYPES: readonly string[];
|
|
356
|
+
/**
|
|
357
|
+
* The widths an image is offered at by default.
|
|
358
|
+
*
|
|
359
|
+
* These are 11ty/image's own defaults, and deliberately so: decision-10 makes
|
|
360
|
+
* that plugin the reference for the markup and the width set, so a site that
|
|
361
|
+
* builds the same `content/` with Eleventy and runs the plugin over the same
|
|
362
|
+
* originals gets an equivalent `srcset` rather than a different one.
|
|
363
|
+
*/
|
|
364
|
+
export declare const DEFAULT_IMAGE_WIDTHS: readonly number[];
|
|
365
|
+
/** The derived formats a site gets without asking. AVIF is not one; see {@link GeekityConfig.imageFormats}. */
|
|
366
|
+
export declare const DEFAULT_IMAGE_FORMATS: readonly string[];
|
|
367
|
+
/**
|
|
368
|
+
* The formats a site may ask for.
|
|
369
|
+
*
|
|
370
|
+
* Every one of them is something sharp can write and every browser that
|
|
371
|
+
* matters can read. There is no `gif` and no `svg`: an animated GIF cannot
|
|
372
|
+
* survive being resized frame by frame into a still, and an SVG has no
|
|
373
|
+
* intrinsic pixels to resize at all, so neither is ever varianted.
|
|
374
|
+
*/
|
|
375
|
+
export declare const KNOWN_IMAGE_FORMATS: readonly string[];
|
|
376
|
+
/** Failed sign-ins allowed before the admin locks a username or an address out. */
|
|
377
|
+
export declare const DEFAULT_LOGIN_ATTEMPTS = 5;
|
|
378
|
+
/** How long the first lockout lasts by default: a quarter of an hour, in seconds. */
|
|
379
|
+
export declare const DEFAULT_LOGIN_LOCKOUT: number;
|
|
160
380
|
/**
|
|
161
381
|
* Identity helper that gives a `geekity.config.ts` file type checking and
|
|
162
382
|
* completion without the site importing the type by hand.
|