emdash 0.21.0 → 0.22.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/dist/{adapters-BxSmgtbF.d.mts → adapters-hUy8vOB-.d.mts} +1 -1
- package/dist/{adapters-BxSmgtbF.d.mts.map → adapters-hUy8vOB-.d.mts.map} +1 -1
- package/dist/after-B1IIdH3Y.mjs +29 -0
- package/dist/after-B1IIdH3Y.mjs.map +1 -0
- package/dist/{allowed-origins-BqC8cul8.mjs → allowed-origins-DjlhyfNN.mjs} +2 -2
- package/dist/{allowed-origins-BqC8cul8.mjs.map → allowed-origins-DjlhyfNN.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +20 -19
- package/dist/api/route-utils.mjs.map +1 -1
- package/dist/api/schemas/index.d.mts +2 -2
- package/dist/api/schemas/index.mjs +4 -4
- package/dist/{api-DxjIV2o8.mjs → api-BDuM4bEy.mjs} +31 -25
- package/dist/api-BDuM4bEy.mjs.map +1 -0
- package/dist/{api-tokens-BFFkB0jB.mjs → api-tokens-ViYKeIp9.mjs} +13 -3
- package/dist/api-tokens-ViYKeIp9.mjs.map +1 -0
- package/dist/{apply-CLjxheyb.mjs → apply-wDqQoxX4.mjs} +18 -18
- package/dist/{apply-CLjxheyb.mjs.map → apply-wDqQoxX4.mjs.map} +1 -1
- package/dist/astro/image-endpoint.d.mts +8 -0
- package/dist/astro/image-endpoint.d.mts.map +1 -0
- package/dist/astro/image-endpoint.mjs +55 -0
- package/dist/astro/image-endpoint.mjs.map +1 -0
- package/dist/astro/index.d.mts +36 -11
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +114 -24
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +9 -9
- package/dist/astro/middleware/auth.d.mts.map +1 -1
- package/dist/astro/middleware/auth.mjs +12 -10
- package/dist/astro/middleware/auth.mjs.map +1 -1
- package/dist/astro/middleware/redirect.mjs +6 -6
- package/dist/astro/middleware/request-context.mjs +5 -5
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +153 -79
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +7 -7
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +7 -7
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +5 -5
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -6
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +6 -6
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +10 -10
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +10 -10
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +10 -10
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +17 -16
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +17 -16
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/admin/bylines/index.mjs +17 -16
- package/dist/astro/routes/api/admin/bylines/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +15 -13
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs.map +1 -1
- package/dist/astro/routes/api/admin/comments/_id_.mjs +8 -6
- package/dist/astro/routes/api/admin/comments/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/admin/comments/bulk.mjs +12 -10
- package/dist/astro/routes/api/admin/comments/bulk.mjs.map +1 -1
- package/dist/astro/routes/api/admin/comments/counts.mjs +8 -6
- package/dist/astro/routes/api/admin/comments/counts.mjs.map +1 -1
- package/dist/astro/routes/api/admin/comments/index.mjs +12 -10
- package/dist/astro/routes/api/admin/comments/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +6 -6
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +5 -5
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +5 -5
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +5 -5
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +40 -39
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +40 -39
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +39 -38
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +39 -38
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +39 -38
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +39 -38
- package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +4 -4
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +39 -38
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +39 -38
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +39 -38
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +39 -38
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +40 -39
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +39 -38
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +40 -39
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +39 -38
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +39 -38
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +4 -4
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +39 -38
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +4 -4
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +3 -3
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +8 -8
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +5 -5
- package/dist/astro/routes/api/admin/users/index.mjs +7 -7
- package/dist/astro/routes/api/auth/dev-bypass.mjs +6 -6
- package/dist/astro/routes/api/auth/invite/accept.mjs +3 -3
- package/dist/astro/routes/api/auth/invite/complete.mjs +12 -12
- package/dist/astro/routes/api/auth/invite/index.mjs +9 -9
- package/dist/astro/routes/api/auth/invite/register-options.mjs +11 -11
- package/dist/astro/routes/api/auth/logout.mjs +4 -4
- package/dist/astro/routes/api/auth/magic-link/send.mjs +11 -11
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +4 -4
- package/dist/astro/routes/api/auth/me.mjs +8 -8
- package/dist/astro/routes/api/auth/mode.mjs +1 -1
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +7 -7
- package/dist/astro/routes/api/auth/passkey/index.mjs +3 -3
- package/dist/astro/routes/api/auth/passkey/options.mjs +13 -13
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +11 -11
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +12 -12
- package/dist/astro/routes/api/auth/passkey/verify.mjs +12 -12
- package/dist/astro/routes/api/auth/signup/complete.mjs +12 -12
- package/dist/astro/routes/api/auth/signup/request.mjs +11 -11
- package/dist/astro/routes/api/auth/signup/verify.mjs +3 -3
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +16 -15
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs.map +1 -1
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.d.mts +9 -0
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.d.mts.map +1 -0
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +179 -0
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs.map +1 -0
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +13 -13
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +17 -15
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/authors.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/index.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/trash.mjs +8 -8
- package/dist/astro/routes/api/dashboard.mjs +10 -8
- package/dist/astro/routes/api/dashboard.mjs.map +1 -1
- package/dist/astro/routes/api/dev/emails.mjs +4 -4
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +12 -12
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +5 -5
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +9 -9
- package/dist/astro/routes/api/import/wordpress/execute.mjs +16 -15
- package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/media.mjs +10 -10
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +11 -11
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +10 -10
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +12 -12
- package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +18 -17
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
- package/dist/astro/routes/api/manifest.mjs +5 -5
- package/dist/astro/routes/api/mcp.mjs +213 -46
- package/dist/astro/routes/api/mcp.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +8 -8
- package/dist/astro/routes/api/media/_id_.mjs +8 -8
- package/dist/astro/routes/api/media/file/_...key_.mjs +3 -3
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +4 -4
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +4 -4
- package/dist/astro/routes/api/media/providers/index.mjs +4 -4
- package/dist/astro/routes/api/media/upload-url.mjs +10 -10
- package/dist/astro/routes/api/media.mjs +13 -13
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +11 -9
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items.mjs +11 -9
- package/dist/astro/routes/api/menus/_name_/items.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +11 -9
- package/dist/astro/routes/api/menus/_name_/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/translations.mjs +11 -9
- package/dist/astro/routes/api/menus/_name_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_.mjs +11 -9
- package/dist/astro/routes/api/menus/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/menus/index.mjs +11 -9
- package/dist/astro/routes/api/menus/index.mjs.map +1 -1
- package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/authorize.mjs +7 -7
- package/dist/astro/routes/api/oauth/device/code.mjs +10 -10
- package/dist/astro/routes/api/oauth/device/token.mjs +9 -9
- package/dist/astro/routes/api/oauth/register.mjs +4 -4
- package/dist/astro/routes/api/oauth/token/refresh.mjs +7 -7
- package/dist/astro/routes/api/oauth/token/revoke.mjs +7 -7
- package/dist/astro/routes/api/oauth/token.mjs +7 -7
- package/dist/astro/routes/api/openapi.json.mjs +5 -5
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +5 -5
- package/dist/astro/routes/api/redirects/404s/index.mjs +11 -11
- package/dist/astro/routes/api/redirects/404s/summary.mjs +11 -11
- package/dist/astro/routes/api/redirects/_id_.mjs +12 -12
- package/dist/astro/routes/api/redirects/index.mjs +12 -12
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +4 -4
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +4 -4
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +39 -38
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +39 -38
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +39 -38
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +39 -38
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +39 -38
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/index.mjs +7 -7
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +39 -38
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +39 -38
- package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/enable.mjs +11 -11
- package/dist/astro/routes/api/search/index.mjs +10 -10
- package/dist/astro/routes/api/search/rebuild.mjs +11 -11
- package/dist/astro/routes/api/search/stats.mjs +7 -7
- package/dist/astro/routes/api/search/suggest.mjs +10 -10
- package/dist/astro/routes/api/sections/_slug_.mjs +10 -10
- package/dist/astro/routes/api/sections/index.mjs +10 -10
- package/dist/astro/routes/api/settings/email.mjs +6 -6
- package/dist/astro/routes/api/settings.mjs +16 -15
- package/dist/astro/routes/api/settings.mjs.map +1 -1
- package/dist/astro/routes/api/setup/admin-verify.mjs +13 -13
- package/dist/astro/routes/api/setup/admin.mjs +12 -12
- package/dist/astro/routes/api/setup/dev-bypass.d.mts.map +1 -1
- package/dist/astro/routes/api/setup/dev-bypass.mjs +29 -27
- package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/setup/dev-reset.mjs +4 -4
- package/dist/astro/routes/api/setup/index.mjs +29 -28
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- package/dist/astro/routes/api/setup/status.mjs +5 -5
- package/dist/astro/routes/api/snapshot.d.mts.map +1 -1
- package/dist/astro/routes/api/snapshot.mjs +10 -9
- package/dist/astro/routes/api/snapshot.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +16 -14
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +16 -14
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +16 -14
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/index.mjs +16 -14
- package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -1
- package/dist/astro/routes/api/themes/preview.mjs +8 -8
- package/dist/astro/routes/api/typegen.mjs +6 -6
- package/dist/astro/routes/api/well-known/auth.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +8 -8
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +11 -11
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +11 -11
- package/dist/astro/routes/api/widget-areas/_name_.mjs +7 -7
- package/dist/astro/routes/api/widget-areas/index.mjs +11 -11
- package/dist/astro/routes/api/widget-components.mjs +4 -4
- package/dist/astro/routes/robots.txt.mjs +10 -9
- package/dist/astro/routes/robots.txt.mjs.map +1 -1
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +13 -12
- package/dist/astro/routes/sitemap-_collection_.xml.mjs.map +1 -1
- package/dist/astro/routes/sitemap.xml.mjs +11 -10
- package/dist/astro/routes/sitemap.xml.mjs.map +1 -1
- package/dist/astro/types.d.mts +16 -12
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{authorize-D5gfBVU5.mjs → authorize-C0EYoUeV.mjs} +2 -2
- package/dist/{authorize-D5gfBVU5.mjs.map → authorize-C0EYoUeV.mjs.map} +1 -1
- package/dist/{base64-CqR-7kqF.mjs → base64-CmWvODNW.mjs} +1 -1
- package/dist/{base64-CqR-7kqF.mjs.map → base64-CmWvODNW.mjs.map} +1 -1
- package/dist/{byline-V_Qp1Ziw.mjs → byline-CC0WS4Gu.mjs} +17 -8
- package/dist/byline-CC0WS4Gu.mjs.map +1 -0
- package/dist/{byline-fields-B0NO1yUB.mjs → byline-fields-53i9Et0x.mjs} +3 -3
- package/dist/{byline-fields-B0NO1yUB.mjs.map → byline-fields-53i9Et0x.mjs.map} +1 -1
- package/dist/{byline-fields-nBVqK_Ff.mjs → byline-fields-DNp0HNKc.mjs} +2 -2
- package/dist/{byline-fields-nBVqK_Ff.mjs.map → byline-fields-DNp0HNKc.mjs.map} +1 -1
- package/dist/{byline-fields-CQJRIQkn.d.mts → byline-fields-jy07PZJM.d.mts} +36 -31
- package/dist/byline-fields-jy07PZJM.d.mts.map +1 -0
- package/dist/{byline-registry-DedidtqC.mjs → byline-registry-BOjqDOim.mjs} +3 -3
- package/dist/{byline-registry-DedidtqC.mjs.map → byline-registry-BOjqDOim.mjs.map} +1 -1
- package/dist/{bylines-DfGDnred.mjs → bylines-CCO1CYnH.mjs} +8 -8
- package/dist/{bylines-DfGDnred.mjs.map → bylines-CCO1CYnH.mjs.map} +1 -1
- package/dist/{bylines-B2NWnIwS.mjs → bylines-DGekMGGm.mjs} +10 -4
- package/dist/{bylines-B2NWnIwS.mjs.map → bylines-DGekMGGm.mjs.map} +1 -1
- package/dist/{cache-DTTHWD8n.mjs → cache-pXTpun6s.mjs} +3 -3
- package/dist/{cache-DTTHWD8n.mjs.map → cache-pXTpun6s.mjs.map} +1 -1
- package/dist/{challenge-store-woE0bbCf.mjs → challenge-store-C6E_kWTs.mjs} +1 -1
- package/dist/{challenge-store-woE0bbCf.mjs.map → challenge-store-C6E_kWTs.mjs.map} +1 -1
- package/dist/{chunks-BerYVuve.mjs → chunks-SyjZaYwV.mjs} +2 -2
- package/dist/{chunks-BerYVuve.mjs.map → chunks-SyjZaYwV.mjs.map} +1 -1
- package/dist/cli/index.mjs +27 -26
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +2 -1
- package/dist/client/index.mjs.map +1 -1
- package/dist/{comment-sqQxNpN3.mjs → comment-80UyJJUR.mjs} +11 -3
- package/dist/comment-80UyJJUR.mjs.map +1 -0
- package/dist/comment-reaction-B6AdcpXw.mjs +85 -0
- package/dist/comment-reaction-B6AdcpXw.mjs.map +1 -0
- package/dist/{comments-D2hNuxNa.mjs → comments-CtPkT2tp.mjs} +3 -3
- package/dist/{comments-D2hNuxNa.mjs.map → comments-CtPkT2tp.mjs.map} +1 -1
- package/dist/{components-DYKp2gmo.mjs → components-D6ZnrzbB.mjs} +1 -1
- package/dist/{components-DYKp2gmo.mjs.map → components-D6ZnrzbB.mjs.map} +1 -1
- package/dist/{content-BIlVx-RX.mjs → content-2PTDNnoh.mjs} +22 -7
- package/dist/content-2PTDNnoh.mjs.map +1 -0
- package/dist/{context-Cm4pt1Ws.mjs → context-f1__jpzF.mjs} +11 -11
- package/dist/{context-Cm4pt1Ws.mjs.map → context-f1__jpzF.mjs.map} +1 -1
- package/dist/{cron-DdEVrQ2Y.mjs → cron-CQPxBjYs.mjs} +1 -1
- package/dist/{cron-DdEVrQ2Y.mjs.map → cron-CQPxBjYs.mjs.map} +1 -1
- package/dist/{dashboard-C-UYpps0.mjs → dashboard-BOESNcwE.mjs} +4 -4
- package/dist/{dashboard-C-UYpps0.mjs.map → dashboard-BOESNcwE.mjs.map} +1 -1
- package/dist/database/instrumentation.d.mts +19 -0
- package/dist/database/instrumentation.d.mts.map +1 -1
- package/dist/database/instrumentation.mjs +8 -1
- package/dist/database/instrumentation.mjs.map +1 -1
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +1 -1
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{db-errors-BluWkwGI.mjs → db-errors-CrC5w3Jz.mjs} +1 -1
- package/dist/{db-errors-BluWkwGI.mjs.map → db-errors-CrC5w3Jz.mjs.map} +1 -1
- package/dist/{default-NHGuJzQ3.mjs → default-CpBEPn6a.mjs} +1 -1
- package/dist/{default-NHGuJzQ3.mjs.map → default-CpBEPn6a.mjs.map} +1 -1
- package/dist/{device-flow-BQApWgnW.mjs → device-flow-Bn-qiDrw.mjs} +5 -5
- package/dist/{device-flow-BQApWgnW.mjs.map → device-flow-Bn-qiDrw.mjs.map} +1 -1
- package/dist/{email-console-BbU3RbWv.mjs → email-console-B9VS6eN4.mjs} +1 -1
- package/dist/{email-console-BbU3RbWv.mjs.map → email-console-B9VS6eN4.mjs.map} +1 -1
- package/dist/{error-CNn_w7jf.mjs → error-BQ1pxs0L.mjs} +2 -2
- package/dist/{error-CNn_w7jf.mjs.map → error-BQ1pxs0L.mjs.map} +1 -1
- package/dist/{escape-DPgcxcpL.mjs → escape-BtbJWyaW.mjs} +1 -1
- package/dist/{escape-DPgcxcpL.mjs.map → escape-BtbJWyaW.mjs.map} +1 -1
- package/dist/{fts-manager-Cx5z8jdA.mjs → fts-manager-DtiWqwNJ.mjs} +2 -2
- package/dist/{fts-manager-Cx5z8jdA.mjs.map → fts-manager-DtiWqwNJ.mjs.map} +1 -1
- package/dist/{hash-DlvIFn0b.mjs → hash-BowcuPwv.mjs} +1 -1
- package/dist/{hash-DlvIFn0b.mjs.map → hash-BowcuPwv.mjs.map} +1 -1
- package/dist/{import-KyxT1Mbs.mjs → import-DqSGOlmR.mjs} +4 -4
- package/dist/{import-KyxT1Mbs.mjs.map → import-DqSGOlmR.mjs.map} +1 -1
- package/dist/{index-D2VAiumu.d.mts → index-C2dLNT6S.d.mts} +172 -17
- package/dist/index-C2dLNT6S.d.mts.map +1 -0
- package/dist/{index-uT2yR66F.d.mts → index-ZDEwR7qW.d.mts} +3 -3
- package/dist/{index-uT2yR66F.d.mts.map → index-ZDEwR7qW.d.mts.map} +1 -1
- package/dist/index.d.mts +18 -17
- package/dist/index.mjs +63 -61
- package/dist/{init-lock-DlBHjf9-.mjs → init-lock-6b309ZrF.mjs} +2 -29
- package/dist/init-lock-6b309ZrF.mjs.map +1 -0
- package/dist/{load-Dq91b_DK.mjs → load-Dtoo7KcJ.mjs} +2 -2
- package/dist/{load-Dq91b_DK.mjs.map → load-Dtoo7KcJ.mjs.map} +1 -1
- package/dist/{loader-BqWjcH3h.mjs → loader-C8Z48Uof.mjs} +4 -4
- package/dist/{loader-BqWjcH3h.mjs.map → loader-C8Z48Uof.mjs.map} +1 -1
- package/dist/{manifest-schema-DFPeqMAn.mjs → manifest-schema-DDSbwkAL.mjs} +1 -1
- package/dist/{manifest-schema-DFPeqMAn.mjs.map → manifest-schema-DDSbwkAL.mjs.map} +1 -1
- package/dist/media/image-endpoint.d.mts +74 -0
- package/dist/media/image-endpoint.d.mts.map +1 -0
- package/dist/media/image-endpoint.mjs +162 -0
- package/dist/media/image-endpoint.mjs.map +1 -0
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +2 -2
- package/dist/media/local-runtime.d.mts +11 -11
- package/dist/media/local-runtime.mjs +9 -8
- package/dist/media/local-runtime.mjs.map +1 -1
- package/dist/{media-JOf3pNkw.mjs → media-Bw0sA6rb.mjs} +2 -2
- package/dist/{media-JOf3pNkw.mjs.map → media-Bw0sA6rb.mjs.map} +1 -1
- package/dist/{media-allowlist-_A0SuDn4.mjs → media-allowlist-DunzrKFM.mjs} +2 -2
- package/dist/{media-allowlist-_A0SuDn4.mjs.map → media-allowlist-DunzrKFM.mjs.map} +1 -1
- package/dist/{media-url-CqLd69IO.mjs → media-url-S22B6aPr.mjs} +1 -1
- package/dist/{media-url-CqLd69IO.mjs.map → media-url-S22B6aPr.mjs.map} +1 -1
- package/dist/{menus-DX4_E01q.mjs → menus-CYs0WZoL.mjs} +20 -6
- package/dist/menus-CYs0WZoL.mjs.map +1 -0
- package/dist/{menus-Ryk9L7fT.mjs → menus-DOs1MQJg.mjs} +120 -20
- package/dist/menus-DOs1MQJg.mjs.map +1 -0
- package/dist/{mime-YbtlEtvS.mjs → mime-DYpsOAny.mjs} +1 -1
- package/dist/{mime-YbtlEtvS.mjs.map → mime-DYpsOAny.mjs.map} +1 -1
- package/dist/{mode-CGXzIbD8.mjs → mode-CFh8Dw8G.mjs} +1 -1
- package/dist/{mode-CGXzIbD8.mjs.map → mode-CFh8Dw8G.mjs.map} +1 -1
- package/dist/{normalize-DKsg36ty.mjs → normalize-Uam4_Vkr.mjs} +1 -1
- package/dist/{normalize-DKsg36ty.mjs.map → normalize-Uam4_Vkr.mjs.map} +1 -1
- package/dist/{oauth-authorization-C2kVyjXI.mjs → oauth-authorization-BN-t83Uy.mjs} +5 -5
- package/dist/{oauth-authorization-C2kVyjXI.mjs.map → oauth-authorization-BN-t83Uy.mjs.map} +1 -1
- package/dist/{oauth-clients-BC873NCV.mjs → oauth-clients-R6I_V0qz.mjs} +1 -1
- package/dist/{oauth-clients-BC873NCV.mjs.map → oauth-clients-R6I_V0qz.mjs.map} +1 -1
- package/dist/{oauth-state-store-Cd--TUaq.mjs → oauth-state-store-Csoc6kkR.mjs} +1 -1
- package/dist/{oauth-state-store-Cd--TUaq.mjs.map → oauth-state-store-Csoc6kkR.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-e4wOvDud.mjs → oauth-user-lookup-d7VYjTpx.mjs} +1 -1
- package/dist/{oauth-user-lookup-e4wOvDud.mjs.map → oauth-user-lookup-d7VYjTpx.mjs.map} +1 -1
- package/dist/object-cache/memory.d.mts +15 -0
- package/dist/object-cache/memory.d.mts.map +1 -0
- package/dist/object-cache/memory.mjs +56 -0
- package/dist/object-cache/memory.mjs.map +1 -0
- package/dist/object-cache-SEb2IM4Z.mjs +373 -0
- package/dist/object-cache-SEb2IM4Z.mjs.map +1 -0
- package/dist/{options-9kLgkE8m.d.mts → options-DFFpvNJU.d.mts} +3 -3
- package/dist/{options-9kLgkE8m.d.mts.map → options-DFFpvNJU.d.mts.map} +1 -1
- package/dist/{options-BPCVnesz.mjs → options-DTTML-Tx.mjs} +1 -1
- package/dist/{options-BPCVnesz.mjs.map → options-DTTML-Tx.mjs.map} +1 -1
- package/dist/page/index.d.mts +2 -2
- package/dist/{parse-DzSrk1t8.mjs → parse-qrHlnzTy.mjs} +2 -2
- package/dist/{parse-DzSrk1t8.mjs.map → parse-qrHlnzTy.mjs.map} +1 -1
- package/dist/{passkey-config-BpjbE_Uv.mjs → passkey-config-BJTbcnI5.mjs} +1 -1
- package/dist/{passkey-config-BpjbE_Uv.mjs.map → passkey-config-BJTbcnI5.mjs.map} +1 -1
- package/dist/{patterns-p-RBdTbM.mjs → patterns-BKmjvM7K.mjs} +1 -1
- package/dist/{patterns-p-RBdTbM.mjs.map → patterns-BKmjvM7K.mjs.map} +1 -1
- package/dist/{placeholder-2xumZh4g.mjs → placeholder-CIDCpyEY.mjs} +1 -1
- package/dist/{placeholder-2xumZh4g.mjs.map → placeholder-CIDCpyEY.mjs.map} +1 -1
- package/dist/{placeholder-BevVKfay.d.mts → placeholder-CirPauNG.d.mts} +1 -1
- package/dist/{placeholder-BevVKfay.d.mts.map → placeholder-CirPauNG.d.mts.map} +1 -1
- package/dist/plugin-types.d.mts +1 -1
- package/dist/plugin-utils.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.mjs +4 -2
- package/dist/plugins/adapt-sandbox-entry.mjs.map +1 -1
- package/dist/{transport-1cIrOb1Y.mjs → portable-text-BIFhrU6T.mjs} +2 -135
- package/dist/portable-text-BIFhrU6T.mjs.map +1 -0
- package/dist/{preview-Dqv2hwXr.mjs → preview-IDye9SPQ.mjs} +2 -2
- package/dist/{preview-Dqv2hwXr.mjs.map → preview-IDye9SPQ.mjs.map} +1 -1
- package/dist/{public-url-D_zARuvZ.mjs → public-url-8lFyQ8ZF.mjs} +1 -1
- package/dist/{public-url-D_zARuvZ.mjs.map → public-url-8lFyQ8ZF.mjs.map} +1 -1
- package/dist/{query-Crm038Mc.mjs → query-B1RVFUNM.mjs} +159 -31
- package/dist/query-B1RVFUNM.mjs.map +1 -0
- package/dist/{rate-limit-hRTBqmw1.mjs → rate-limit-DfNAqIWW.mjs} +2 -2
- package/dist/{rate-limit-hRTBqmw1.mjs.map → rate-limit-DfNAqIWW.mjs.map} +1 -1
- package/dist/{redirect-C-OOkyku.mjs → redirect-B4wuX2A5.mjs} +1 -1
- package/dist/{redirect-C-OOkyku.mjs.map → redirect-B4wuX2A5.mjs.map} +1 -1
- package/dist/{redirect-CRWIt8Zj.mjs → redirect-Bifv_yHv.mjs} +3 -3
- package/dist/{redirect-CRWIt8Zj.mjs.map → redirect-Bifv_yHv.mjs.map} +1 -1
- package/dist/{redirects-CP3TnTLO.mjs → redirects-CT_vb3NV.mjs} +6 -6
- package/dist/{redirects-CP3TnTLO.mjs.map → redirects-CT_vb3NV.mjs.map} +1 -1
- package/dist/{redirects-6Zg2SoYo.mjs → redirects-DUvXgzCX.mjs} +8 -3
- package/dist/redirects-DUvXgzCX.mjs.map +1 -0
- package/dist/{registry-diMzD1Wf.mjs → registry-CjK96fHD.mjs} +6 -6
- package/dist/{registry-diMzD1Wf.mjs.map → registry-CjK96fHD.mjs.map} +1 -1
- package/dist/{request-cache-UwmBAiUK.mjs → request-cache-KCNHp_RJ.mjs} +1 -1
- package/dist/{request-cache-UwmBAiUK.mjs.map → request-cache-KCNHp_RJ.mjs.map} +1 -1
- package/dist/{request-meta-DPechd0W.mjs → request-meta-ei3ATilC.mjs} +2 -2
- package/dist/{request-meta-DPechd0W.mjs.map → request-meta-ei3ATilC.mjs.map} +1 -1
- package/dist/{resolve-B3NUUtVY.mjs → resolve-CpbBglV0.mjs} +1 -1
- package/dist/{resolve-B3NUUtVY.mjs.map → resolve-CpbBglV0.mjs.map} +1 -1
- package/dist/{runner--4wMWwKM.mjs → runner-DYYnW530.mjs} +201 -170
- package/dist/runner-DYYnW530.mjs.map +1 -0
- package/dist/{runner-C8vcbvCe.d.mts → runner-jdXtFEd3.d.mts} +2 -2
- package/dist/{runner-C8vcbvCe.d.mts.map → runner-jdXtFEd3.d.mts.map} +1 -1
- package/dist/runtime.d.mts +10 -10
- package/dist/runtime.mjs +3 -3
- package/dist/{schema-BDOkd3OU.mjs → schema-5z2Jfnbm.mjs} +13 -8
- package/dist/schema-5z2Jfnbm.mjs.map +1 -0
- package/dist/{search-Bs_J_EW-.mjs → search-D9FkoM-k.mjs} +4 -4
- package/dist/{search-Bs_J_EW-.mjs.map → search-D9FkoM-k.mjs.map} +1 -1
- package/dist/{secrets-C8xmE6mR.mjs → secrets-BUE5UQys.mjs} +5 -5
- package/dist/{secrets-C8xmE6mR.mjs.map → secrets-BUE5UQys.mjs.map} +1 -1
- package/dist/{sections-P0zuBlyz.mjs → sections-DWYZo6HK.mjs} +3 -3
- package/dist/{sections-P0zuBlyz.mjs.map → sections-DWYZo6HK.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +22 -21
- package/dist/seo/index.d.mts +1 -1
- package/dist/seo/index.mjs +1 -1
- package/dist/{seo-DpNgGQjF.mjs → seo-Cu47j3mJ.mjs} +5 -2
- package/dist/seo-Cu47j3mJ.mjs.map +1 -0
- package/dist/{seo-CLhm-Fmb.mjs → seo-DRnqnstF.mjs} +1 -1
- package/dist/{seo-CLhm-Fmb.mjs.map → seo-DRnqnstF.mjs.map} +1 -1
- package/dist/{service-CDQQnT8W.mjs → service-DTmtqCnw.mjs} +3 -3
- package/dist/{service-CDQQnT8W.mjs.map → service-DTmtqCnw.mjs.map} +1 -1
- package/dist/session-user-DmtPMNa1.mjs +51 -0
- package/dist/session-user-DmtPMNa1.mjs.map +1 -0
- package/dist/{settings-sO0Fif4p.mjs → settings-DUrVDN0c.mjs} +3 -3
- package/dist/{settings-sO0Fif4p.mjs.map → settings-DUrVDN0c.mjs.map} +1 -1
- package/dist/{settings-BjBsmVAo.mjs → settings-yaxu9mU-.mjs} +18 -10
- package/dist/settings-yaxu9mU-.mjs.map +1 -0
- package/dist/{setup-complete-CMMr-oZU.mjs → setup-complete-BWFHzgvZ.mjs} +2 -2
- package/dist/{setup-complete-CMMr-oZU.mjs.map → setup-complete-BWFHzgvZ.mjs.map} +1 -1
- package/dist/{setup-nonce-169xl4fV.mjs → setup-nonce-DFWG-J5c.mjs} +1 -1
- package/dist/{setup-nonce-169xl4fV.mjs.map → setup-nonce-DFWG-J5c.mjs.map} +1 -1
- package/dist/{single-flight-cache-C0UV1Npg.mjs → single-flight-cache-B5p5cNOL.mjs} +2 -2
- package/dist/{single-flight-cache-C0UV1Npg.mjs.map → single-flight-cache-B5p5cNOL.mjs.map} +1 -1
- package/dist/{site-url-vtsuOvSD.mjs → site-url-BOTPgmeM.mjs} +2 -2
- package/dist/{site-url-vtsuOvSD.mjs.map → site-url-BOTPgmeM.mjs.map} +1 -1
- package/dist/{slugify-Cjh1ssOZ.mjs → slugify-Cce3dTdg.mjs} +1 -1
- package/dist/{slugify-Cjh1ssOZ.mjs.map → slugify-Cce3dTdg.mjs.map} +1 -1
- package/dist/{ssrf-XO05Voq6.mjs → ssrf-B0VeRLrp.mjs} +1 -1
- package/dist/{ssrf-XO05Voq6.mjs.map → ssrf-B0VeRLrp.mjs.map} +1 -1
- package/dist/{status-2gZklYuj.mjs → status-CB2basWJ.mjs} +1 -1
- package/dist/{status-2gZklYuj.mjs.map → status-CB2basWJ.mjs.map} +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/{taxonomies-BBxYA38v.mjs → taxonomies-Be9B1jEj.mjs} +156 -122
- package/dist/taxonomies-Be9B1jEj.mjs.map +1 -0
- package/dist/{taxonomies-DuESHWKI.mjs → taxonomies-CJmkEpdS.mjs} +7 -7
- package/dist/{taxonomies-DuESHWKI.mjs.map → taxonomies-CJmkEpdS.mjs.map} +1 -1
- package/dist/{taxonomy-CdllE4oq.mjs → taxonomy-B2HQuVf5.mjs} +19 -6
- package/dist/taxonomy-B2HQuVf5.mjs.map +1 -0
- package/dist/{tokens-DMkVjxrx.mjs → tokens-BvrJvIB6.mjs} +2 -2
- package/dist/{tokens-DMkVjxrx.mjs.map → tokens-BvrJvIB6.mjs.map} +1 -1
- package/dist/{transaction-x2tJQ-A1.mjs → transaction-qfqpPVpu.mjs} +1 -1
- package/dist/{transaction-x2tJQ-A1.mjs.map → transaction-qfqpPVpu.mjs.map} +1 -1
- package/dist/transport-B4rXnSTf.mjs +135 -0
- package/dist/transport-B4rXnSTf.mjs.map +1 -0
- package/dist/{transport-jdvsZEIt.d.mts → transport-k7YjfmEn.d.mts} +1 -1
- package/dist/{transport-jdvsZEIt.d.mts.map → transport-k7YjfmEn.d.mts.map} +1 -1
- package/dist/{trusted-proxy-CHp41Fjj.mjs → trusted-proxy-DY1_UqHr.mjs} +1 -1
- package/dist/{trusted-proxy-CHp41Fjj.mjs.map → trusted-proxy-DY1_UqHr.mjs.map} +1 -1
- package/dist/{types-DO7whVYU.d.mts → types-CIKX2HO5.d.mts} +2 -2
- package/dist/{types-DO7whVYU.d.mts.map → types-CIKX2HO5.d.mts.map} +1 -1
- package/dist/{types-u_XxjbS8.d.mts → types-CKNXt63n.d.mts} +1 -1
- package/dist/{types-u_XxjbS8.d.mts.map → types-CKNXt63n.d.mts.map} +1 -1
- package/dist/{types-BTnnBYVX.d.mts → types-CWGYDYqF.d.mts} +2 -2
- package/dist/{types-BTnnBYVX.d.mts.map → types-CWGYDYqF.d.mts.map} +1 -1
- package/dist/{types-CkEuk-Zr.d.mts → types-CgvhU-hF.d.mts} +1 -1
- package/dist/{types-CkEuk-Zr.d.mts.map → types-CgvhU-hF.d.mts.map} +1 -1
- package/dist/{types-BFgYtuKd.d.mts → types-CmivgZbp.d.mts} +1 -1
- package/dist/{types-BFgYtuKd.d.mts.map → types-CmivgZbp.d.mts.map} +1 -1
- package/dist/{types-Del0VMij.d.mts → types-Czg1S3gB.d.mts} +9 -1
- package/dist/{types-Del0VMij.d.mts.map → types-Czg1S3gB.d.mts.map} +1 -1
- package/dist/types-DCNNfeCv.d.mts +124 -0
- package/dist/types-DCNNfeCv.d.mts.map +1 -0
- package/dist/{types-Bzfk2yC8.d.mts → types-DPCpkzvS.d.mts} +1 -1
- package/dist/{types-Bzfk2yC8.d.mts.map → types-DPCpkzvS.d.mts.map} +1 -1
- package/dist/{types-DdkL6fyv.d.mts → types-DXLVzV2w.d.mts} +1 -1
- package/dist/{types-DdkL6fyv.d.mts.map → types-DXLVzV2w.d.mts.map} +1 -1
- package/dist/{types-BXSUSAjt.mjs → types-DXOIbece.mjs} +3 -3
- package/dist/{types-BXSUSAjt.mjs.map → types-DXOIbece.mjs.map} +1 -1
- package/dist/{types-DejCHqWT.mjs → types-DYF2_Vf4.mjs} +1 -1
- package/dist/{types-DejCHqWT.mjs.map → types-DYF2_Vf4.mjs.map} +1 -1
- package/dist/{types-BIduXPJk.mjs → types-tM44hEcf.mjs} +1 -1
- package/dist/{types-BIduXPJk.mjs.map → types-tM44hEcf.mjs.map} +1 -1
- package/dist/{user-C0um7wrg.mjs → user-Db-Rti_z.mjs} +3 -3
- package/dist/{user-C0um7wrg.mjs.map → user-Db-Rti_z.mjs.map} +1 -1
- package/dist/{utils-C4M981Br.mjs → utils-LbHcSRTj.mjs} +2 -2
- package/dist/{utils-C4M981Br.mjs.map → utils-LbHcSRTj.mjs.map} +1 -1
- package/dist/{validate-DGhQPXzI.mjs → validate-Bwl64sxv.mjs} +3 -3
- package/dist/{validate-DGhQPXzI.mjs.map → validate-Bwl64sxv.mjs.map} +1 -1
- package/dist/{validate-cJOiOvT2.d.mts → validate-CE2M0nvx.d.mts} +5 -5
- package/dist/{validate-cJOiOvT2.d.mts.map → validate-CE2M0nvx.d.mts.map} +1 -1
- package/dist/{validation-DVHjPM1M.mjs → validation-C_yOvFaO.mjs} +6 -6
- package/dist/{validation-DVHjPM1M.mjs.map → validation-C_yOvFaO.mjs.map} +1 -1
- package/dist/version-RnYXYjzD.mjs +7 -0
- package/dist/{version-BOjj_cfz.mjs.map → version-RnYXYjzD.mjs.map} +1 -1
- package/dist/{widgets-Ci6hLwfO.mjs → widgets-8GONuRHo.mjs} +4 -4
- package/dist/{widgets-Ci6hLwfO.mjs.map → widgets-8GONuRHo.mjs.map} +1 -1
- package/dist/{zod-generator-CarzgPAu.mjs → zod-generator-D481JjzD.mjs} +3 -3
- package/dist/{zod-generator-CarzgPAu.mjs.map → zod-generator-D481JjzD.mjs.map} +1 -1
- package/package.json +18 -6
- package/src/api/handlers/api-tokens.ts +19 -0
- package/src/api/handlers/comment-reactions.ts +165 -0
- package/src/api/handlers/schema.ts +8 -0
- package/src/api/schemas/comments.ts +10 -0
- package/src/astro/image-endpoint.ts +84 -0
- package/src/astro/index.ts +6 -0
- package/src/astro/integration/index.ts +83 -22
- package/src/astro/integration/routes.ts +5 -0
- package/src/astro/integration/runtime.ts +65 -1
- package/src/astro/integration/virtual-modules.ts +32 -0
- package/src/astro/integration/vite-config.ts +16 -0
- package/src/astro/middleware/auth.ts +4 -3
- package/src/astro/middleware/stream-end-metrics.ts +11 -1
- package/src/astro/middleware.ts +41 -3
- package/src/astro/object-cache/adapters.ts +50 -0
- package/src/astro/prefetch.ts +77 -0
- package/src/astro/routes/api/comments/[collection]/[contentId]/reactions.ts +97 -0
- package/src/astro/routes/api/setup/dev-bypass.ts +5 -1
- package/src/astro/routes/api/snapshot.ts +3 -1
- package/src/astro/session-user.ts +57 -0
- package/src/astro/types.ts +1 -0
- package/src/comments/query.ts +78 -7
- package/src/comments/ranking.ts +58 -0
- package/src/components/Comments.astro +136 -1
- package/src/database/instrumentation.ts +21 -1
- package/src/database/migrations/044_comment_reactions.ts +56 -0
- package/src/database/migrations/runner.ts +2 -0
- package/src/database/repositories/byline.ts +13 -0
- package/src/database/repositories/comment-reaction.ts +132 -0
- package/src/database/repositories/comment.ts +10 -0
- package/src/database/repositories/content.ts +25 -3
- package/src/database/repositories/menu.ts +13 -1
- package/src/database/repositories/seo.ts +3 -0
- package/src/database/repositories/taxonomy.ts +13 -1
- package/src/database/types.ts +9 -0
- package/src/emdash-runtime.ts +18 -4
- package/src/index.ts +22 -0
- package/src/mcp/server.ts +307 -20
- package/src/media/image-endpoint.ts +167 -0
- package/src/menus/index.ts +11 -4
- package/src/object-cache/codec.ts +73 -0
- package/src/object-cache/index.ts +495 -0
- package/src/object-cache/memory.ts +91 -0
- package/src/object-cache/types.ts +124 -0
- package/src/plugins/adapt-sandbox-entry.ts +11 -1
- package/src/query.ts +196 -17
- package/src/schema/query.ts +11 -4
- package/src/settings/index.ts +18 -5
- package/src/taxonomies/index.ts +237 -165
- package/src/virtual-modules.d.ts +11 -0
- package/dist/api-DxjIV2o8.mjs.map +0 -1
- package/dist/api-tokens-BFFkB0jB.mjs.map +0 -1
- package/dist/byline-V_Qp1Ziw.mjs.map +0 -1
- package/dist/byline-fields-CQJRIQkn.d.mts.map +0 -1
- package/dist/comment-sqQxNpN3.mjs.map +0 -1
- package/dist/content-BIlVx-RX.mjs.map +0 -1
- package/dist/index-D2VAiumu.d.mts.map +0 -1
- package/dist/init-lock-DlBHjf9-.mjs.map +0 -1
- package/dist/menus-DX4_E01q.mjs.map +0 -1
- package/dist/menus-Ryk9L7fT.mjs.map +0 -1
- package/dist/query-Crm038Mc.mjs.map +0 -1
- package/dist/redirects-6Zg2SoYo.mjs.map +0 -1
- package/dist/runner--4wMWwKM.mjs.map +0 -1
- package/dist/schema-BDOkd3OU.mjs.map +0 -1
- package/dist/seo-DpNgGQjF.mjs.map +0 -1
- package/dist/settings-BjBsmVAo.mjs.map +0 -1
- package/dist/taxonomies-BBxYA38v.mjs.map +0 -1
- package/dist/taxonomy-CdllE4oq.mjs.map +0 -1
- package/dist/transport-1cIrOb1Y.mjs.map +0 -1
- package/dist/version-BOjj_cfz.mjs +0 -7
- /package/dist/{api-tokens-C7ywRx7l.mjs → api-tokens-BVH-H8Z9.mjs} +0 -0
- /package/dist/{ssrf-CRZGzjdL.mjs → ssrf-Cz1e2QPn.mjs} +0 -0
- /package/dist/{types-BoRm8-pp.mjs → types-Bfr2Pj6z.mjs} +0 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public comment reaction endpoints (Tier 1 of the best-in-class comments RFC).
|
|
3
|
+
*
|
|
4
|
+
* GET /_emdash/api/comments/:collection/:contentId/reactions
|
|
5
|
+
* - Aggregate reaction counts for the content's approved comments, plus the
|
|
6
|
+
* current visitor's active reactions.
|
|
7
|
+
* POST /_emdash/api/comments/:collection/:contentId/reactions
|
|
8
|
+
* - Toggle a reaction on a comment. Public, honeypot- and rate-limit-gated.
|
|
9
|
+
*
|
|
10
|
+
* Inherits the `/_emdash/api/comments/` public prefix (no auth); the POST still
|
|
11
|
+
* requires the `X-EmDash-Request: 1` CSRF header like the comment POST.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { APIRoute } from "astro";
|
|
15
|
+
|
|
16
|
+
import { apiError, apiSuccess, handleError, requireDb, unwrapResult } from "#api/error.js";
|
|
17
|
+
import { handleReactionCounts, handleReactionToggle } from "#api/handlers/comment-reactions.js";
|
|
18
|
+
import { hashIp } from "#api/handlers/comments.js";
|
|
19
|
+
import { isParseError, parseBody } from "#api/parse.js";
|
|
20
|
+
import { createReactionBody } from "#api/schemas.js";
|
|
21
|
+
import { resolveSecretsCached } from "#config/secrets.js";
|
|
22
|
+
import { extractRequestMeta } from "#plugins/request-meta.js";
|
|
23
|
+
|
|
24
|
+
export const prerender = false;
|
|
25
|
+
|
|
26
|
+
export const GET: APIRoute = async ({ params, request, locals }) => {
|
|
27
|
+
const { emdash } = locals;
|
|
28
|
+
const { collection, contentId } = params;
|
|
29
|
+
|
|
30
|
+
if (!collection || !contentId) {
|
|
31
|
+
return apiError("VALIDATION_ERROR", "Collection and content ID required", 400);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const dbErr = requireDb(emdash?.db);
|
|
35
|
+
if (dbErr) return dbErr;
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
// Salted voter hash from request IP (same primitive as comment ip_hash).
|
|
39
|
+
// Behind Cloudflare (CF-Connecting-IP) or a configured trusted proxy this
|
|
40
|
+
// is per-visitor. Without a trusted IP it collapses to a shared "unknown"
|
|
41
|
+
// bucket, so reaction dedup degrades for those visitors — a real
|
|
42
|
+
// per-visitor token is Tier 2 (visitor identity). Operators should set
|
|
43
|
+
// trustedProxyHeaders; see the comment ingest route for the same note.
|
|
44
|
+
const meta = extractRequestMeta(request, emdash.config);
|
|
45
|
+
let voterHash = "unknown";
|
|
46
|
+
if (meta.ip) {
|
|
47
|
+
const { ipSalt } = await resolveSecretsCached(emdash.db);
|
|
48
|
+
voterHash = await hashIp(meta.ip, ipSalt);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const result = await handleReactionCounts(emdash.db, collection, contentId, voterHash);
|
|
52
|
+
return unwrapResult(result);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return handleError(error, "Failed to read reactions", "REACTION_COUNTS_ERROR");
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const POST: APIRoute = async ({ params, request, locals }) => {
|
|
59
|
+
const { emdash } = locals;
|
|
60
|
+
const { collection, contentId } = params;
|
|
61
|
+
|
|
62
|
+
if (!collection || !contentId) {
|
|
63
|
+
return apiError("VALIDATION_ERROR", "Collection and content ID required", 400);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const dbErr = requireDb(emdash?.db);
|
|
67
|
+
if (dbErr) return dbErr;
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const body = await parseBody(request, createReactionBody);
|
|
71
|
+
if (isParseError(body)) return body;
|
|
72
|
+
|
|
73
|
+
// Anti-spam: honeypot — hidden field filled only by bots. Silently accept.
|
|
74
|
+
if (body.website_url) {
|
|
75
|
+
return apiSuccess({ reacted: false, counts: {} });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const meta = extractRequestMeta(request, emdash.config);
|
|
79
|
+
let voterHash = "unknown";
|
|
80
|
+
if (meta.ip) {
|
|
81
|
+
const { ipSalt } = await resolveSecretsCached(emdash.db);
|
|
82
|
+
voterHash = await hashIp(meta.ip, ipSalt);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const result = await handleReactionToggle(emdash.db, {
|
|
86
|
+
collection,
|
|
87
|
+
contentId,
|
|
88
|
+
commentId: body.commentId,
|
|
89
|
+
reaction: body.reaction,
|
|
90
|
+
voterHash,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return unwrapResult(result, 200);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return handleError(error, "Failed to toggle reaction", "REACTION_TOGGLE_ERROR");
|
|
96
|
+
}
|
|
97
|
+
};
|
|
@@ -23,7 +23,7 @@ import { ulid } from "ulidx";
|
|
|
23
23
|
|
|
24
24
|
import { apiError, apiSuccess, handleError } from "#api/error.js";
|
|
25
25
|
import { escapeHtml } from "#api/escape.js";
|
|
26
|
-
import { handleApiTokenCreate } from "#api/handlers/api-tokens.js";
|
|
26
|
+
import { deleteApiTokensByName, handleApiTokenCreate } from "#api/handlers/api-tokens.js";
|
|
27
27
|
import { getPublicOrigin } from "#api/public-url.js";
|
|
28
28
|
import { isSafeRedirect } from "#api/redirect.js";
|
|
29
29
|
import { runMigrations } from "#db/migrations/runner.js";
|
|
@@ -134,6 +134,10 @@ async function handleDevBypass(context: Parameters<APIRoute>[0]): Promise<Respon
|
|
|
134
134
|
// Optionally create a PAT token (?token=1) for headless/CLI testing.
|
|
135
135
|
let token: string | undefined;
|
|
136
136
|
if (url.searchParams.has("token")) {
|
|
137
|
+
// Idempotent by name: a prior reset can leave a stale dev-bypass-token,
|
|
138
|
+
// and the raw token is only available at creation, so drop any existing
|
|
139
|
+
// one and mint a fresh, usable PAT rather than accumulating duplicates.
|
|
140
|
+
await deleteApiTokensByName(emdash.db, user.id, "dev-bypass-token");
|
|
137
141
|
const result = await handleApiTokenCreate(emdash.db, user.id, {
|
|
138
142
|
name: "dev-bypass-token",
|
|
139
143
|
scopes: [
|
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
import { getPublicOrigin } from "#api/public-url.js";
|
|
21
21
|
import { resolveSecretsCached } from "#config/secrets.js";
|
|
22
22
|
|
|
23
|
+
import { resolveSessionUser } from "../../session-user.js";
|
|
24
|
+
|
|
23
25
|
export const prerender = false;
|
|
24
26
|
|
|
25
27
|
export const GET: APIRoute = async ({ request, locals, url, session }) => {
|
|
@@ -31,7 +33,7 @@ export const GET: APIRoute = async ({ request, locals, url, session }) => {
|
|
|
31
33
|
if (!user && session && emdash?.db) {
|
|
32
34
|
try {
|
|
33
35
|
const { createKyselyAdapter } = await import("@emdash-cms/auth/adapters/kysely");
|
|
34
|
-
const sessionUser = await session
|
|
36
|
+
const sessionUser = await resolveSessionUser(session);
|
|
35
37
|
if (sessionUser?.id) {
|
|
36
38
|
const adapter = createKyselyAdapter(emdash.db);
|
|
37
39
|
const resolved = await adapter.getUserById(sessionUser.id);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { after } from "../after.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Backstop timeout for resolving the session user. A live session-store read
|
|
5
|
+
* settles in a few ms, so this only ever fires on a genuinely stalled read.
|
|
6
|
+
*/
|
|
7
|
+
export const SESSION_GET_TIMEOUT_MS = 3_000;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the Astro session user without risking an isolate-wide hang.
|
|
11
|
+
*
|
|
12
|
+
* On Cloudflare Workers, a request cancelled mid-`session.get()` (client
|
|
13
|
+
* disconnect, context teardown) can leave the underlying session-store read as
|
|
14
|
+
* a promise that never settles — neither resolving nor rejecting. Awaiting it
|
|
15
|
+
* directly hangs the request, and because the stalled promise is shared at the
|
|
16
|
+
* isolate level, every later session-bearing request hangs too (observed as
|
|
17
|
+
* 0-CPU, multi-minute, `canceled` responses; see #1274). A surrounding
|
|
18
|
+
* try/catch cannot help: the promise never rejects.
|
|
19
|
+
*
|
|
20
|
+
* Two layers, mirroring the reclaimable-cache pattern used elsewhere in core:
|
|
21
|
+
* 1. `after()` anchors the read so a cancelled request still drives it to
|
|
22
|
+
* completion — the promise settles and the isolate is not poisoned for
|
|
23
|
+
* subsequent requests (prevents the hang rather than merely surviving it).
|
|
24
|
+
* 2. A timeout is a fail-closed backstop: a still-stalled (or rejecting) read
|
|
25
|
+
* resolves to `undefined`, and every caller treats the absence of a session
|
|
26
|
+
* user as unauthenticated (anonymous on public routes, 401/redirect on
|
|
27
|
+
* protected ones). It can only ever drop privileges for that one request,
|
|
28
|
+
* never grant them.
|
|
29
|
+
*
|
|
30
|
+
* Used by every session read on the request path: the main middleware (the
|
|
31
|
+
* first read on a session-bearing request), the auth middleware, and the
|
|
32
|
+
* preview-snapshot route (which bypasses the auth middleware).
|
|
33
|
+
*/
|
|
34
|
+
export async function resolveSessionUser<T>(
|
|
35
|
+
session: { get(key: "user"): Promise<T> } | undefined,
|
|
36
|
+
timeoutMs = SESSION_GET_TIMEOUT_MS,
|
|
37
|
+
): Promise<T | undefined> {
|
|
38
|
+
if (!session) return undefined;
|
|
39
|
+
const read: Promise<T | undefined> = Promise.resolve(session.get("user")).catch(() => undefined);
|
|
40
|
+
// Keep the worker alive past response/cancellation so the read completes and
|
|
41
|
+
// the shared promise settles — this is what prevents the isolate poisoning.
|
|
42
|
+
after(() =>
|
|
43
|
+
read.then(
|
|
44
|
+
() => undefined,
|
|
45
|
+
() => undefined,
|
|
46
|
+
),
|
|
47
|
+
);
|
|
48
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
49
|
+
const timeout = new Promise<undefined>((resolve) => {
|
|
50
|
+
timer = setTimeout(resolve, timeoutMs, undefined);
|
|
51
|
+
});
|
|
52
|
+
try {
|
|
53
|
+
return await Promise.race([read, timeout]);
|
|
54
|
+
} finally {
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/astro/types.ts
CHANGED
package/src/comments/query.ts
CHANGED
|
@@ -7,15 +7,26 @@
|
|
|
7
7
|
|
|
8
8
|
import type { Kysely } from "kysely";
|
|
9
9
|
|
|
10
|
+
import { CommentReactionRepository } from "../database/repositories/comment-reaction.js";
|
|
10
11
|
import { CommentRepository } from "../database/repositories/comment.js";
|
|
11
12
|
import type { PublicComment } from "../database/repositories/comment.js";
|
|
12
13
|
import type { Database } from "../database/types.js";
|
|
13
14
|
import { getDb } from "../loader.js";
|
|
15
|
+
import { cachedQuery, CacheNamespace } from "../object-cache/index.js";
|
|
16
|
+
import { reactionScore } from "./ranking.js";
|
|
14
17
|
|
|
15
18
|
export interface GetCommentsOptions {
|
|
16
19
|
collection: string;
|
|
17
20
|
contentId: string;
|
|
18
21
|
threaded?: boolean;
|
|
22
|
+
/** Attach aggregate reaction counts to each comment. */
|
|
23
|
+
reactions?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Order of top-level comments. `oldest` (default) preserves the existing
|
|
26
|
+
* chronological display; `best` ranks by Wilson-scored reactions (implies
|
|
27
|
+
* `reactions`). Replies always stay chronological.
|
|
28
|
+
*/
|
|
29
|
+
sort?: "oldest" | "best";
|
|
19
30
|
}
|
|
20
31
|
|
|
21
32
|
export interface GetCommentsResult {
|
|
@@ -38,8 +49,21 @@ export interface GetCommentsResult {
|
|
|
38
49
|
* ```
|
|
39
50
|
*/
|
|
40
51
|
export async function getComments(options: GetCommentsOptions): Promise<GetCommentsResult> {
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
// The result varies by every option getCommentsWithDb branches on, so all of
|
|
53
|
+
// them belong in the key. `best` implies reactions, so normalize the reaction
|
|
54
|
+
// flag — `{ sort: "best" }` and `{ sort: "best", reactions: true }` produce
|
|
55
|
+
// identical output and should share one entry.
|
|
56
|
+
const sort = options.sort ?? "oldest";
|
|
57
|
+
const withReactions = options.reactions || sort === "best";
|
|
58
|
+
const threaded = options.threaded ? "t" : "f";
|
|
59
|
+
return cachedQuery({
|
|
60
|
+
namespace: CacheNamespace.COMMENTS,
|
|
61
|
+
key: `comments:${options.collection}:${options.contentId}:${threaded}:${withReactions ? "r" : "n"}:${sort}`,
|
|
62
|
+
load: async () => {
|
|
63
|
+
const db = await getDb();
|
|
64
|
+
return getCommentsWithDb(db, options);
|
|
65
|
+
},
|
|
66
|
+
});
|
|
43
67
|
}
|
|
44
68
|
|
|
45
69
|
/**
|
|
@@ -65,16 +89,63 @@ export async function getCommentsWithDb(
|
|
|
65
89
|
limit: MAX_COMMENTS,
|
|
66
90
|
});
|
|
67
91
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
const items: PublicComment[] = options.threaded
|
|
93
|
+
? CommentRepository.assembleThreads(result.items).map((c) =>
|
|
94
|
+
CommentRepository.toPublicComment(c),
|
|
95
|
+
)
|
|
96
|
+
: result.items.map((c) => CommentRepository.toPublicComment(c));
|
|
97
|
+
|
|
98
|
+
// `best` sort needs reaction data, so it implies reactions.
|
|
99
|
+
if (options.reactions || options.sort === "best") {
|
|
100
|
+
await attachReactions(db, items);
|
|
101
|
+
if (options.sort === "best") {
|
|
102
|
+
sortByBest(items);
|
|
103
|
+
}
|
|
72
104
|
}
|
|
73
105
|
|
|
74
|
-
const items = result.items.map((c) => CommentRepository.toPublicComment(c));
|
|
75
106
|
return { items, total };
|
|
76
107
|
}
|
|
77
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Attach aggregate reaction counts to a list of public comments (and their
|
|
111
|
+
* replies), in a single batched query.
|
|
112
|
+
*/
|
|
113
|
+
async function attachReactions(db: Kysely<Database>, items: PublicComment[]): Promise<void> {
|
|
114
|
+
const ids: string[] = [];
|
|
115
|
+
for (const comment of items) {
|
|
116
|
+
ids.push(comment.id);
|
|
117
|
+
if (comment.replies) {
|
|
118
|
+
for (const reply of comment.replies) ids.push(reply.id);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (ids.length === 0) return;
|
|
122
|
+
|
|
123
|
+
const counts = await new CommentReactionRepository(db).countsForComments(ids);
|
|
124
|
+
|
|
125
|
+
const assign = (comment: PublicComment) => {
|
|
126
|
+
const reactions = counts.get(comment.id);
|
|
127
|
+
if (reactions) comment.reactions = reactions;
|
|
128
|
+
};
|
|
129
|
+
for (const comment of items) {
|
|
130
|
+
assign(comment);
|
|
131
|
+
comment.replies?.forEach(assign);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Sort top-level comments by Wilson-scored reactions (descending), tie-broken
|
|
137
|
+
* by oldest-first to keep ordering stable.
|
|
138
|
+
*/
|
|
139
|
+
function sortByBest(items: PublicComment[]): void {
|
|
140
|
+
items.sort((a, b) => {
|
|
141
|
+
const scoreDelta = reactionScore(b.reactions ?? {}) - reactionScore(a.reactions ?? {});
|
|
142
|
+
if (scoreDelta !== 0) return scoreDelta;
|
|
143
|
+
if (a.createdAt < b.createdAt) return -1;
|
|
144
|
+
if (a.createdAt > b.createdAt) return 1;
|
|
145
|
+
return 0;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
78
149
|
/**
|
|
79
150
|
* Get the count of approved comments for a content item.
|
|
80
151
|
*
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comment ranking utilities (Tier 1 of the best-in-class comments RFC).
|
|
3
|
+
*
|
|
4
|
+
* Wilson score lower-bound (95% confidence) — the same primitive Reddit uses
|
|
5
|
+
* for its "Best" comment sort. Ranks by the statistical lower bound of the
|
|
6
|
+
* positive-reaction proportion rather than the raw count, so a comment with a
|
|
7
|
+
* couple of reactions can't outrank a heavily-reacted one until it earns
|
|
8
|
+
* confidence, and a late-but-popular comment still rises (submission time is
|
|
9
|
+
* irrelevant).
|
|
10
|
+
*
|
|
11
|
+
* Positive-only reactions (the recommended default) degrade gracefully: with
|
|
12
|
+
* `down = 0` the score still increases monotonically with `up` while penalising
|
|
13
|
+
* low-sample comments — wilson(1,0) ≈ 0.21, wilson(10,0) ≈ 0.73,
|
|
14
|
+
* wilson(200,0) ≈ 0.98.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** z for a 95% two-sided confidence interval. */
|
|
18
|
+
const DEFAULT_Z = 1.96;
|
|
19
|
+
|
|
20
|
+
/** Reactions that count against a comment when both signals are in use. */
|
|
21
|
+
const NEGATIVE_REACTIONS: ReadonlySet<string> = new Set(["dislike", "down"]);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Wilson score lower bound of the positive proportion.
|
|
25
|
+
*
|
|
26
|
+
* @param up count of positive reactions
|
|
27
|
+
* @param down count of negative reactions
|
|
28
|
+
* @returns a score in [0, 1]; 0 when there are no reactions
|
|
29
|
+
*/
|
|
30
|
+
export function wilsonLowerBound(up: number, down: number, z: number = DEFAULT_Z): number {
|
|
31
|
+
const n = up + down;
|
|
32
|
+
if (n <= 0) return 0;
|
|
33
|
+
const phat = up / n;
|
|
34
|
+
const z2 = z * z;
|
|
35
|
+
const denom = 1 + z2 / n;
|
|
36
|
+
const centre = phat + z2 / (2 * n);
|
|
37
|
+
const margin = z * Math.sqrt((phat * (1 - phat) + z2 / (4 * n)) / n);
|
|
38
|
+
return (centre - margin) / denom;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Reduce a per-reaction count map to a single rank score via the Wilson
|
|
43
|
+
* lower bound. Any reaction not in {@link NEGATIVE_REACTIONS} is treated as
|
|
44
|
+
* positive, so the positive-only default (just `like`) works without special
|
|
45
|
+
* casing.
|
|
46
|
+
*/
|
|
47
|
+
export function reactionScore(counts: Record<string, number>): number {
|
|
48
|
+
let up = 0;
|
|
49
|
+
let down = 0;
|
|
50
|
+
for (const [reaction, count] of Object.entries(counts)) {
|
|
51
|
+
if (NEGATIVE_REACTIONS.has(reaction)) {
|
|
52
|
+
down += count;
|
|
53
|
+
} else {
|
|
54
|
+
up += count;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return wilsonLowerBound(up, down);
|
|
58
|
+
}
|
|
@@ -21,6 +21,10 @@ interface Props {
|
|
|
21
21
|
collection: string;
|
|
22
22
|
contentId: string;
|
|
23
23
|
threaded?: boolean;
|
|
24
|
+
/** Show a like/reaction button per comment (opt-in). */
|
|
25
|
+
reactions?: boolean;
|
|
26
|
+
/** Order of top-level comments: chronological (default) or Wilson-ranked. */
|
|
27
|
+
sort?: "oldest" | "best";
|
|
24
28
|
class?: string;
|
|
25
29
|
}
|
|
26
30
|
|
|
@@ -28,13 +32,15 @@ const {
|
|
|
28
32
|
collection,
|
|
29
33
|
contentId,
|
|
30
34
|
threaded = false,
|
|
35
|
+
reactions = false,
|
|
36
|
+
sort = "oldest",
|
|
31
37
|
class: className,
|
|
32
38
|
} = Astro.props;
|
|
33
39
|
|
|
34
40
|
const collectionInfo = await getCollectionInfo(collection);
|
|
35
41
|
const enabled = collectionInfo?.commentsEnabled ?? false;
|
|
36
42
|
const { items, total } = enabled
|
|
37
|
-
? await getComments({ collection, contentId, threaded })
|
|
43
|
+
? await getComments({ collection, contentId, threaded, reactions, sort })
|
|
38
44
|
: { items: [], total: 0 };
|
|
39
45
|
|
|
40
46
|
const URL_RE = /https?:\/\/[^\s<>"')\]]+/g;
|
|
@@ -62,15 +68,72 @@ function escapeHtml(text: string): string {
|
|
|
62
68
|
function formatBody(text: string): string {
|
|
63
69
|
return autoLinkUrls(escapeHtml(text));
|
|
64
70
|
}
|
|
71
|
+
|
|
72
|
+
// Progressive-enhancement script for reactions. Emitted verbatim (is:inline)
|
|
73
|
+
// only when `reactions` is enabled, so non-reaction pages ship zero extra JS.
|
|
74
|
+
// Avoids template literals internally so it survives the outer backticks.
|
|
75
|
+
const REACTION_SCRIPT = `
|
|
76
|
+
(() => {
|
|
77
|
+
const sections = document.querySelectorAll('[data-ec-comments][data-ec-reactions]');
|
|
78
|
+
sections.forEach((section) => {
|
|
79
|
+
const collection = section.getAttribute('data-collection');
|
|
80
|
+
const contentId = section.getAttribute('data-content-id');
|
|
81
|
+
if (!collection || !contentId) return;
|
|
82
|
+
const base = '/_emdash/api/comments/' + encodeURIComponent(collection) + '/' + encodeURIComponent(contentId) + '/reactions';
|
|
83
|
+
const headers = { 'X-EmDash-Request': '1' };
|
|
84
|
+
|
|
85
|
+
fetch(base, { headers: headers })
|
|
86
|
+
.then((r) => (r.ok ? r.json() : null))
|
|
87
|
+
.then((payload) => {
|
|
88
|
+
const viewer = payload && payload.data && payload.data.viewer;
|
|
89
|
+
if (!viewer) return;
|
|
90
|
+
Object.keys(viewer).forEach((commentId) => {
|
|
91
|
+
(viewer[commentId] || []).forEach((reaction) => {
|
|
92
|
+
const sel = '.ec-reaction[data-comment-id="' + commentId + '"][data-ec-reaction="' + reaction + '"]';
|
|
93
|
+
const btn = section.querySelector(sel);
|
|
94
|
+
if (btn) btn.setAttribute('aria-pressed', 'true');
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
})
|
|
98
|
+
.catch(() => {});
|
|
99
|
+
|
|
100
|
+
section.addEventListener('click', (event) => {
|
|
101
|
+
const btn = event.target.closest('.ec-reaction');
|
|
102
|
+
if (!btn || !section.contains(btn)) return;
|
|
103
|
+
const commentId = btn.getAttribute('data-comment-id');
|
|
104
|
+
const reaction = btn.getAttribute('data-ec-reaction');
|
|
105
|
+
if (!commentId || !reaction) return;
|
|
106
|
+
btn.disabled = true;
|
|
107
|
+
fetch(base, {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
headers: Object.assign({ 'Content-Type': 'application/json' }, headers),
|
|
110
|
+
body: JSON.stringify({ commentId: commentId, reaction: reaction }),
|
|
111
|
+
})
|
|
112
|
+
.then((r) => (r.ok ? r.json() : null))
|
|
113
|
+
.then((payload) => {
|
|
114
|
+
const data = payload && payload.data;
|
|
115
|
+
if (!data) return;
|
|
116
|
+
btn.setAttribute('aria-pressed', data.reacted ? 'true' : 'false');
|
|
117
|
+
const countEl = btn.querySelector('[data-ec-reaction-count]');
|
|
118
|
+
if (countEl) countEl.textContent = String((data.counts && data.counts[reaction]) || 0);
|
|
119
|
+
})
|
|
120
|
+
.catch(() => {})
|
|
121
|
+
.finally(() => { btn.disabled = false; });
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
})();
|
|
125
|
+
`;
|
|
65
126
|
---
|
|
66
127
|
|
|
67
128
|
{
|
|
68
129
|
enabled && (
|
|
130
|
+
<Fragment>
|
|
69
131
|
<section
|
|
70
132
|
class:list={["ec-comments", className]}
|
|
71
133
|
data-ec-comments
|
|
72
134
|
data-collection={collection}
|
|
73
135
|
data-content-id={contentId}
|
|
136
|
+
{...(reactions ? { "data-ec-reactions": "" } : {})}
|
|
74
137
|
>
|
|
75
138
|
<h3 class="ec-comments-heading">
|
|
76
139
|
{total === 0
|
|
@@ -112,6 +175,25 @@ function formatBody(text: string): string {
|
|
|
112
175
|
class="ec-comment-body"
|
|
113
176
|
set:html={formatBody(comment.body)}
|
|
114
177
|
/>
|
|
178
|
+
{reactions && (
|
|
179
|
+
<div class="ec-comment-actions">
|
|
180
|
+
<button
|
|
181
|
+
type="button"
|
|
182
|
+
class="ec-reaction"
|
|
183
|
+
data-ec-reaction="like"
|
|
184
|
+
data-comment-id={comment.id}
|
|
185
|
+
aria-pressed="false"
|
|
186
|
+
>
|
|
187
|
+
<span class="ec-reaction-icon" aria-hidden="true">
|
|
188
|
+
♡
|
|
189
|
+
</span>
|
|
190
|
+
<span class="ec-reaction-label">Like</span>
|
|
191
|
+
<span class="ec-reaction-count" data-ec-reaction-count>
|
|
192
|
+
{comment.reactions?.like ?? 0}
|
|
193
|
+
</span>
|
|
194
|
+
</button>
|
|
195
|
+
</div>
|
|
196
|
+
)}
|
|
115
197
|
{threaded && comment.replies && comment.replies.length > 0 && (
|
|
116
198
|
<ol class="ec-comment-replies">
|
|
117
199
|
{comment.replies.map((reply: PublicComment) => (
|
|
@@ -153,6 +235,21 @@ function formatBody(text: string): string {
|
|
|
153
235
|
class="ec-comment-body"
|
|
154
236
|
set:html={formatBody(reply.body)}
|
|
155
237
|
/>
|
|
238
|
+
{reactions && (
|
|
239
|
+
<div class="ec-comment-actions">
|
|
240
|
+
<button
|
|
241
|
+
type="button"
|
|
242
|
+
class="ec-reaction"
|
|
243
|
+
data-ec-reaction="like"
|
|
244
|
+
data-comment-id={reply.id}
|
|
245
|
+
aria-pressed="false"
|
|
246
|
+
>
|
|
247
|
+
<span class="ec-reaction-icon" aria-hidden="true">♡</span>
|
|
248
|
+
<span class="ec-reaction-label">Like</span>
|
|
249
|
+
<span class="ec-reaction-count" data-ec-reaction-count>{reply.reactions?.like ?? 0}</span>
|
|
250
|
+
</button>
|
|
251
|
+
</div>
|
|
252
|
+
)}
|
|
156
253
|
</article>
|
|
157
254
|
</li>
|
|
158
255
|
))}
|
|
@@ -164,6 +261,8 @@ function formatBody(text: string): string {
|
|
|
164
261
|
</ol>
|
|
165
262
|
)}
|
|
166
263
|
</section>
|
|
264
|
+
{reactions && <script is:inline set:html={REACTION_SCRIPT} />}
|
|
265
|
+
</Fragment>
|
|
167
266
|
)
|
|
168
267
|
}
|
|
169
268
|
|
|
@@ -229,4 +328,40 @@ function formatBody(text: string): string {
|
|
|
229
328
|
white-space: pre-wrap;
|
|
230
329
|
word-break: break-word;
|
|
231
330
|
}
|
|
331
|
+
|
|
332
|
+
.ec-comment-actions {
|
|
333
|
+
margin-top: 0.5rem;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.ec-reaction {
|
|
337
|
+
display: inline-flex;
|
|
338
|
+
align-items: center;
|
|
339
|
+
gap: 0.35rem;
|
|
340
|
+
font: inherit;
|
|
341
|
+
font-size: 0.875em;
|
|
342
|
+
color: inherit;
|
|
343
|
+
background: none;
|
|
344
|
+
border: var(--ec-comment-border);
|
|
345
|
+
border-radius: 999px;
|
|
346
|
+
padding: 0.15rem 0.6rem;
|
|
347
|
+
cursor: pointer;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.ec-reaction[aria-pressed="true"] {
|
|
351
|
+
border-color: currentColor;
|
|
352
|
+
font-weight: 600;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.ec-reaction[aria-pressed="true"] .ec-reaction-icon {
|
|
356
|
+
color: #e0245e;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.ec-reaction:disabled {
|
|
360
|
+
opacity: 0.5;
|
|
361
|
+
cursor: default;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.ec-reaction-count {
|
|
365
|
+
font-variant-numeric: tabular-nums;
|
|
366
|
+
}
|
|
232
367
|
</style>
|
|
@@ -33,6 +33,19 @@ export interface QueryRecorder {
|
|
|
33
33
|
route: string;
|
|
34
34
|
method: string;
|
|
35
35
|
phase: string;
|
|
36
|
+
/**
|
|
37
|
+
* Set once the recorder has been emitted, so a flush is idempotent.
|
|
38
|
+
* Without this, a fallback flush and the stream-end flush could both
|
|
39
|
+
* fire and double-emit the events.
|
|
40
|
+
*/
|
|
41
|
+
flushed?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Set when the response body is wrapped for stream-end metrics. The
|
|
44
|
+
* recorder is then flushed when the body finishes streaming (so it
|
|
45
|
+
* captures queries issued by components during streaming) rather than
|
|
46
|
+
* when middleware returns (headers ready, body not yet streamed).
|
|
47
|
+
*/
|
|
48
|
+
deferredFlush?: boolean;
|
|
36
49
|
}
|
|
37
50
|
|
|
38
51
|
export function createRecorder(route: string, method: string, phase: string): QueryRecorder {
|
|
@@ -60,9 +73,16 @@ export function recordEvent(
|
|
|
60
73
|
* harness pipes the child's stdout, filters lines beginning with
|
|
61
74
|
* QUERY_LOG_PREFIX, and writes them to its own file. Using stdout means
|
|
62
75
|
* the sink works uniformly in Node and in workerd (which has no fs).
|
|
76
|
+
*
|
|
77
|
+
* Idempotent: the first call emits and marks the recorder flushed, later
|
|
78
|
+
* calls no-op. For streamed responses the flush is deferred to stream end
|
|
79
|
+
* (see wrapBodyForStreamMetrics) so it captures queries issued while the
|
|
80
|
+
* body is still rendering; bodyless responses fall back to a flush when
|
|
81
|
+
* middleware returns.
|
|
63
82
|
*/
|
|
64
83
|
export function flushRecorder(rec: QueryRecorder): void {
|
|
65
|
-
if (rec.
|
|
84
|
+
if (rec.flushed) return;
|
|
85
|
+
rec.flushed = true;
|
|
66
86
|
for (const e of rec.events) {
|
|
67
87
|
console.log(`${QUERY_LOG_PREFIX} ${JSON.stringify(e)}`);
|
|
68
88
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Kysely } from "kysely";
|
|
2
|
+
|
|
3
|
+
import { currentTimestamp } from "../dialect-helpers.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Comment reactions (Tier 1 of the best-in-class comments RFC).
|
|
7
|
+
*
|
|
8
|
+
* One row per (comment, voter, reaction). Toggle semantics are enforced by the
|
|
9
|
+
* unique index — a voter either has a given reaction on a comment or doesn't.
|
|
10
|
+
* `voter_hash` is a salted SHA-256 of the visitor's IP (same primitive as
|
|
11
|
+
* `_emdash_comments.ip_hash`), so no cleartext identity is stored.
|
|
12
|
+
*
|
|
13
|
+
* Additive and backward-compatible: a new table, no change to existing schema.
|
|
14
|
+
*/
|
|
15
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
16
|
+
await db.schema
|
|
17
|
+
.createTable("_emdash_comment_reactions")
|
|
18
|
+
.ifNotExists()
|
|
19
|
+
.addColumn("id", "text", (col) => col.primaryKey())
|
|
20
|
+
.addColumn("comment_id", "text", (col) =>
|
|
21
|
+
col.notNull().references("_emdash_comments.id").onDelete("cascade"),
|
|
22
|
+
)
|
|
23
|
+
.addColumn("reaction", "text", (col) => col.notNull().defaultTo("like"))
|
|
24
|
+
.addColumn("voter_hash", "text", (col) => col.notNull())
|
|
25
|
+
.addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db)))
|
|
26
|
+
.execute();
|
|
27
|
+
|
|
28
|
+
// One reaction of a given type per voter per comment (toggle semantics).
|
|
29
|
+
await db.schema
|
|
30
|
+
.createIndex("idx_comment_reactions_unique")
|
|
31
|
+
.ifNotExists()
|
|
32
|
+
.on("_emdash_comment_reactions")
|
|
33
|
+
.columns(["comment_id", "voter_hash", "reaction"])
|
|
34
|
+
.unique()
|
|
35
|
+
.execute();
|
|
36
|
+
|
|
37
|
+
// Aggregate counts per comment (GROUP BY comment_id, reaction).
|
|
38
|
+
await db.schema
|
|
39
|
+
.createIndex("idx_comment_reactions_comment")
|
|
40
|
+
.ifNotExists()
|
|
41
|
+
.on("_emdash_comment_reactions")
|
|
42
|
+
.column("comment_id")
|
|
43
|
+
.execute();
|
|
44
|
+
|
|
45
|
+
// Per-voter rate limiting (recent reactions by a voter).
|
|
46
|
+
await db.schema
|
|
47
|
+
.createIndex("idx_comment_reactions_voter")
|
|
48
|
+
.ifNotExists()
|
|
49
|
+
.on("_emdash_comment_reactions")
|
|
50
|
+
.columns(["voter_hash", "created_at"])
|
|
51
|
+
.execute();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
55
|
+
await db.schema.dropTable("_emdash_comment_reactions").execute();
|
|
56
|
+
}
|