emdash 0.21.0 → 0.23.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-DAAjOID4.d.mts} +1 -1
- package/dist/{adapters-BxSmgtbF.d.mts.map → adapters-DAAjOID4.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-D5FxMUo8.mjs} +2 -2
- package/dist/{allowed-origins-BqC8cul8.mjs.map → allowed-origins-D5FxMUo8.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-BQ_yKRdG.mjs} +31 -25
- package/dist/api-BQ_yKRdG.mjs.map +1 -0
- package/dist/{api-tokens-BFFkB0jB.mjs → api-tokens-D6ppjIVi.mjs} +13 -3
- package/dist/api-tokens-D6ppjIVi.mjs.map +1 -0
- package/dist/{apply-CLjxheyb.mjs → apply-CWRyxIoO.mjs} +18 -18
- package/dist/{apply-CLjxheyb.mjs.map → apply-CWRyxIoO.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 +122 -26
- 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.d.mts.map +1 -1
- package/dist/astro/middleware/request-context.mjs +8 -6
- package/dist/astro/middleware/request-context.mjs.map +1 -1
- 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 +444 -149
- 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 +18 -16
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +18 -16
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/admin/bylines/index.mjs +18 -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 +41 -39
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +41 -39
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +40 -38
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +40 -38
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +40 -38
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +40 -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 +40 -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 +40 -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 +40 -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 +40 -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 +41 -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 +40 -38
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +41 -39
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +40 -38
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +40 -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 +40 -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 +17 -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 +19 -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 +40 -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 +40 -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 +40 -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 +40 -38
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +40 -38
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/index.mjs +9 -8
- package/dist/astro/routes/api/schema/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +40 -38
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +40 -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 +30 -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 +30 -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-Cse0k7B0.mjs} +2 -2
- package/dist/{authorize-D5gfBVU5.mjs.map → authorize-Cse0k7B0.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-DVo-liaP.mjs} +27 -76
- package/dist/byline-DVo-liaP.mjs.map +1 -0
- package/dist/{byline-fields-B0NO1yUB.mjs → byline-fields-B-KXkg5R.mjs} +3 -3
- package/dist/{byline-fields-B0NO1yUB.mjs.map → byline-fields-B-KXkg5R.mjs.map} +1 -1
- package/dist/{byline-fields-nBVqK_Ff.mjs → byline-fields-DaMKzkhO.mjs} +2 -2
- package/dist/{byline-fields-nBVqK_Ff.mjs.map → byline-fields-DaMKzkhO.mjs.map} +1 -1
- package/dist/{byline-fields-CQJRIQkn.d.mts → byline-fields-RFPkDLRw.d.mts} +56 -31
- package/dist/byline-fields-RFPkDLRw.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-f5ppBpD5.mjs} +8 -8
- package/dist/{bylines-DfGDnred.mjs.map → bylines-f5ppBpD5.mjs.map} +1 -1
- package/dist/{bylines-B2NWnIwS.mjs → bylines-pIKgc_TL.mjs} +10 -4
- package/dist/{bylines-B2NWnIwS.mjs.map → bylines-pIKgc_TL.mjs.map} +1 -1
- package/dist/{cache-DTTHWD8n.mjs → cache-Q6V4U-Xz.mjs} +3 -3
- package/dist/{cache-DTTHWD8n.mjs.map → cache-Q6V4U-Xz.mjs.map} +1 -1
- package/dist/{challenge-store-woE0bbCf.mjs → challenge-store-LhiqMccz.mjs} +1 -1
- package/dist/{challenge-store-woE0bbCf.mjs.map → challenge-store-LhiqMccz.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 +29 -27
- 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-Dyb-AutY.mjs +85 -0
- package/dist/comment-reaction-Dyb-AutY.mjs.map +1 -0
- package/dist/{comments-D2hNuxNa.mjs → comments-Cczx7AvD.mjs} +3 -3
- package/dist/{comments-D2hNuxNa.mjs.map → comments-Cczx7AvD.mjs.map} +1 -1
- package/dist/{components-DYKp2gmo.mjs → components-CYt4uVK9.mjs} +1 -1
- package/dist/{components-DYKp2gmo.mjs.map → components-CYt4uVK9.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-B1W8soLv.mjs} +64 -26
- package/dist/context-B1W8soLv.mjs.map +1 -0
- package/dist/{cron-DdEVrQ2Y.mjs → cron-C5LVoNmP.mjs} +14 -2
- package/dist/cron-C5LVoNmP.mjs.map +1 -0
- package/dist/{dashboard-C-UYpps0.mjs → dashboard-CYjpQZ6-.mjs} +4 -4
- package/dist/{dashboard-C-UYpps0.mjs.map → dashboard-CYjpQZ6-.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-UXB1CK42.mjs} +1 -1
- package/dist/{db-errors-BluWkwGI.mjs.map → db-errors-UXB1CK42.mjs.map} +1 -1
- package/dist/{default-NHGuJzQ3.mjs → default-CeRG-Ot4.mjs} +1 -1
- package/dist/{default-NHGuJzQ3.mjs.map → default-CeRG-Ot4.mjs.map} +1 -1
- package/dist/{device-flow-BQApWgnW.mjs → device-flow-DENDCQ9F.mjs} +5 -5
- package/dist/{device-flow-BQApWgnW.mjs.map → device-flow-DENDCQ9F.mjs.map} +1 -1
- package/dist/{email-console-BbU3RbWv.mjs → email-console-DJP32ucW.mjs} +1 -1
- package/dist/{email-console-BbU3RbWv.mjs.map → email-console-DJP32ucW.mjs.map} +1 -1
- package/dist/{error-CNn_w7jf.mjs → error-Bl6kZgtt.mjs} +2 -2
- package/dist/{error-CNn_w7jf.mjs.map → error-Bl6kZgtt.mjs.map} +1 -1
- package/dist/{escape-DPgcxcpL.mjs → escape-Bjio4ZsM.mjs} +1 -1
- package/dist/{escape-DPgcxcpL.mjs.map → escape-Bjio4ZsM.mjs.map} +1 -1
- package/dist/field-defs-cache-ztrdzUGb.mjs +77 -0
- package/dist/field-defs-cache-ztrdzUGb.mjs.map +1 -0
- package/dist/{fts-manager-Cx5z8jdA.mjs → fts-manager-CQuHt-vS.mjs} +2 -2
- package/dist/{fts-manager-Cx5z8jdA.mjs.map → fts-manager-CQuHt-vS.mjs.map} +1 -1
- package/dist/{hash-DlvIFn0b.mjs → hash-DrvzIXcz.mjs} +1 -1
- package/dist/{hash-DlvIFn0b.mjs.map → hash-DrvzIXcz.mjs.map} +1 -1
- package/dist/{import-KyxT1Mbs.mjs → import-Bb1T9WJS.mjs} +4 -4
- package/dist/{import-KyxT1Mbs.mjs.map → import-Bb1T9WJS.mjs.map} +1 -1
- package/dist/{index-D2VAiumu.d.mts → index-BaIj5hIq.d.mts} +198 -21
- package/dist/index-BaIj5hIq.d.mts.map +1 -0
- package/dist/{index-uT2yR66F.d.mts → index-qbL02uhs.d.mts} +3 -3
- package/dist/{index-uT2yR66F.d.mts.map → index-qbL02uhs.d.mts.map} +1 -1
- package/dist/index.d.mts +18 -17
- package/dist/index.mjs +64 -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-BNo-ER8C.mjs} +2 -2
- package/dist/{load-Dq91b_DK.mjs.map → load-BNo-ER8C.mjs.map} +1 -1
- package/dist/{loader-BqWjcH3h.mjs → loader-Dn74CG1b.mjs} +70 -11
- package/dist/loader-Dn74CG1b.mjs.map +1 -0
- package/dist/{manifest-schema-DFPeqMAn.mjs → manifest-schema-BWKnQYQF.mjs} +1 -1
- package/dist/{manifest-schema-DFPeqMAn.mjs.map → manifest-schema-BWKnQYQF.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 +19 -11
- package/dist/media/local-runtime.d.mts.map +1 -1
- package/dist/media/local-runtime.mjs +16 -13
- 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-B77I5_bw.mjs} +2 -2
- package/dist/{media-allowlist-_A0SuDn4.mjs.map → media-allowlist-B77I5_bw.mjs.map} +1 -1
- package/dist/{media-url-CqLd69IO.mjs → media-url-O4rm9-SQ.mjs} +1 -1
- package/dist/{media-url-CqLd69IO.mjs.map → media-url-O4rm9-SQ.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-DS8VTZgo.mjs} +167 -24
- package/dist/menus-DS8VTZgo.mjs.map +1 -0
- package/dist/{mime-YbtlEtvS.mjs → mime-BTzvzJ8M.mjs} +1 -1
- package/dist/{mime-YbtlEtvS.mjs.map → mime-BTzvzJ8M.mjs.map} +1 -1
- package/dist/{mode-CGXzIbD8.mjs → mode-BB0F8xTC.mjs} +1 -1
- package/dist/{mode-CGXzIbD8.mjs.map → mode-BB0F8xTC.mjs.map} +1 -1
- package/dist/{normalize-DKsg36ty.mjs → normalize-ClbNIenm.mjs} +1 -1
- package/dist/{normalize-DKsg36ty.mjs.map → normalize-ClbNIenm.mjs.map} +1 -1
- package/dist/{oauth-authorization-C2kVyjXI.mjs → oauth-authorization-CsvzIp_F.mjs} +5 -5
- package/dist/{oauth-authorization-C2kVyjXI.mjs.map → oauth-authorization-CsvzIp_F.mjs.map} +1 -1
- package/dist/{oauth-clients-BC873NCV.mjs → oauth-clients-C9SYwEbZ.mjs} +1 -1
- package/dist/{oauth-clients-BC873NCV.mjs.map → oauth-clients-C9SYwEbZ.mjs.map} +1 -1
- package/dist/{oauth-state-store-Cd--TUaq.mjs → oauth-state-store---zrApfB.mjs} +1 -1
- package/dist/{oauth-state-store-Cd--TUaq.mjs.map → oauth-state-store---zrApfB.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-e4wOvDud.mjs → oauth-user-lookup-SHsWRlG9.mjs} +1 -1
- package/dist/{oauth-user-lookup-e4wOvDud.mjs.map → oauth-user-lookup-SHsWRlG9.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-CqECG0gM.d.mts} +3 -3
- package/dist/{options-9kLgkE8m.d.mts.map → options-CqECG0gM.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-C9hpjUyY.mjs} +2 -2
- package/dist/{parse-DzSrk1t8.mjs.map → parse-C9hpjUyY.mjs.map} +1 -1
- package/dist/{passkey-config-BpjbE_Uv.mjs → passkey-config-C0YfSBko.mjs} +1 -1
- package/dist/{passkey-config-BpjbE_Uv.mjs.map → passkey-config-C0YfSBko.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-B0s2mWu5.mjs} +1 -1
- package/dist/{placeholder-2xumZh4g.mjs.map → placeholder-B0s2mWu5.mjs.map} +1 -1
- package/dist/{placeholder-BevVKfay.d.mts → placeholder-QoI4gEzP.d.mts} +1 -1
- package/dist/{placeholder-BevVKfay.d.mts.map → placeholder-QoI4gEzP.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-BICg8bTk.mjs} +2 -135
- package/dist/portable-text-BICg8bTk.mjs.map +1 -0
- package/dist/{preview-Dqv2hwXr.mjs → preview-DKGCt2_p.mjs} +2 -2
- package/dist/{preview-Dqv2hwXr.mjs.map → preview-DKGCt2_p.mjs.map} +1 -1
- package/dist/{public-url-D_zARuvZ.mjs → public-url-CTVqgMmg.mjs} +1 -1
- package/dist/{public-url-D_zARuvZ.mjs.map → public-url-CTVqgMmg.mjs.map} +1 -1
- package/dist/{query-Crm038Mc.mjs → query-DgUHZLWB.mjs} +230 -31
- package/dist/query-DgUHZLWB.mjs.map +1 -0
- package/dist/{rate-limit-hRTBqmw1.mjs → rate-limit-BuQ5d4X_.mjs} +2 -2
- package/dist/{rate-limit-hRTBqmw1.mjs.map → rate-limit-BuQ5d4X_.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/{redirect-C-OOkyku.mjs → redirect-CS-PHtNh.mjs} +1 -1
- package/dist/{redirect-C-OOkyku.mjs.map → redirect-CS-PHtNh.mjs.map} +1 -1
- package/dist/{redirects-CP3TnTLO.mjs → redirects-BZ8Mxn8H.mjs} +6 -6
- package/dist/{redirects-CP3TnTLO.mjs.map → redirects-BZ8Mxn8H.mjs.map} +1 -1
- package/dist/{redirects-6Zg2SoYo.mjs → redirects-x9eFPeyi.mjs} +10 -3
- package/dist/redirects-x9eFPeyi.mjs.map +1 -0
- package/dist/{registry-diMzD1Wf.mjs → registry-CK1SS6pi.mjs} +6 -6
- package/dist/{registry-diMzD1Wf.mjs.map → registry-CK1SS6pi.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-CmS1tDFf.mjs} +2 -2
- package/dist/{request-meta-DPechd0W.mjs.map → request-meta-CmS1tDFf.mjs.map} +1 -1
- package/dist/{resolve-B3NUUtVY.mjs → resolve-C7I0qiR0.mjs} +1 -1
- package/dist/{resolve-B3NUUtVY.mjs.map → resolve-C7I0qiR0.mjs.map} +1 -1
- package/dist/{runner-C8vcbvCe.d.mts → runner-CEtq-6Sm.d.mts} +2 -2
- package/dist/{runner-C8vcbvCe.d.mts.map → runner-CEtq-6Sm.d.mts.map} +1 -1
- package/dist/{runner--4wMWwKM.mjs → runner-DYYnW530.mjs} +201 -170
- package/dist/runner-DYYnW530.mjs.map +1 -0
- package/dist/runtime.d.mts +10 -10
- package/dist/runtime.mjs +3 -3
- package/dist/{schema-BDOkd3OU.mjs → schema-CbFRFoM7.mjs} +13 -8
- package/dist/schema-CbFRFoM7.mjs.map +1 -0
- package/dist/{search-Bs_J_EW-.mjs → search-C-waE_YW.mjs} +4 -4
- package/dist/{search-Bs_J_EW-.mjs.map → search-C-waE_YW.mjs.map} +1 -1
- package/dist/{secrets-C8xmE6mR.mjs → secrets-BSf9pRRY.mjs} +5 -5
- package/dist/{secrets-C8xmE6mR.mjs.map → secrets-BSf9pRRY.mjs.map} +1 -1
- package/dist/{sections-P0zuBlyz.mjs → sections-osX9U-YJ.mjs} +3 -3
- package/dist/{sections-P0zuBlyz.mjs.map → sections-osX9U-YJ.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +23 -21
- package/dist/seo/index.d.mts +1 -1
- package/dist/seo/index.mjs +1 -1
- package/dist/{seo-DpNgGQjF.mjs → seo-4FovMwNS.mjs} +5 -2
- package/dist/seo-4FovMwNS.mjs.map +1 -0
- package/dist/{seo-CLhm-Fmb.mjs → seo-DCEF-AVr.mjs} +1 -1
- package/dist/{seo-CLhm-Fmb.mjs.map → seo-DCEF-AVr.mjs.map} +1 -1
- package/dist/{service-CDQQnT8W.mjs → service-jby1RRmU.mjs} +3 -3
- package/dist/{service-CDQQnT8W.mjs.map → service-jby1RRmU.mjs.map} +1 -1
- package/dist/session-user-B8aLtKAH.mjs +51 -0
- package/dist/session-user-B8aLtKAH.mjs.map +1 -0
- package/dist/{settings-sO0Fif4p.mjs → settings-4bRT2NIf.mjs} +3 -3
- package/dist/{settings-sO0Fif4p.mjs.map → settings-4bRT2NIf.mjs.map} +1 -1
- package/dist/{settings-BjBsmVAo.mjs → settings-t-H2jPuJ.mjs} +18 -10
- package/dist/settings-t-H2jPuJ.mjs.map +1 -0
- package/dist/{setup-complete-CMMr-oZU.mjs → setup-complete-gEiySUc-.mjs} +2 -2
- package/dist/{setup-complete-CMMr-oZU.mjs.map → setup-complete-gEiySUc-.mjs.map} +1 -1
- package/dist/{setup-nonce-169xl4fV.mjs → setup-nonce-DzS50zme.mjs} +1 -1
- package/dist/{setup-nonce-169xl4fV.mjs.map → setup-nonce-DzS50zme.mjs.map} +1 -1
- package/dist/{single-flight-cache-C0UV1Npg.mjs → single-flight-cache-Cdfkic3t.mjs} +2 -2
- package/dist/{single-flight-cache-C0UV1Npg.mjs.map → single-flight-cache-Cdfkic3t.mjs.map} +1 -1
- package/dist/{site-url-vtsuOvSD.mjs → site-url-BLebyON8.mjs} +2 -2
- package/dist/{site-url-vtsuOvSD.mjs.map → site-url-BLebyON8.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-BRKb343l.mjs} +1 -1
- package/dist/{ssrf-XO05Voq6.mjs.map → ssrf-BRKb343l.mjs.map} +1 -1
- package/dist/{status-2gZklYuj.mjs → status-vUK0SA17.mjs} +1 -1
- package/dist/{status-2gZklYuj.mjs.map → status-vUK0SA17.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-DuESHWKI.mjs → taxonomies-BBB8O-CU.mjs} +7 -7
- package/dist/{taxonomies-DuESHWKI.mjs.map → taxonomies-BBB8O-CU.mjs.map} +1 -1
- package/dist/{taxonomies-BBxYA38v.mjs → taxonomies-Dxrc_szG.mjs} +177 -123
- package/dist/taxonomies-Dxrc_szG.mjs.map +1 -0
- package/dist/{taxonomy-CdllE4oq.mjs → taxonomy-B2HQuVf5.mjs} +19 -6
- package/dist/taxonomy-B2HQuVf5.mjs.map +1 -0
- package/dist/{tokens-DMkVjxrx.mjs → tokens-LeuXF9gG.mjs} +2 -2
- package/dist/{tokens-DMkVjxrx.mjs.map → tokens-LeuXF9gG.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-jdvsZEIt.d.mts → transport-BZ-03aFT.d.mts} +1 -1
- package/dist/{transport-jdvsZEIt.d.mts.map → transport-BZ-03aFT.d.mts.map} +1 -1
- package/dist/transport-_2nBz7e9.mjs +135 -0
- package/dist/transport-_2nBz7e9.mjs.map +1 -0
- package/dist/{trusted-proxy-CHp41Fjj.mjs → trusted-proxy-DZY5WCn2.mjs} +1 -1
- package/dist/{trusted-proxy-CHp41Fjj.mjs.map → trusted-proxy-DZY5WCn2.mjs.map} +1 -1
- package/dist/{types-Del0VMij.d.mts → types-B-gIxFCE.d.mts} +9 -1
- package/dist/{types-Del0VMij.d.mts.map → types-B-gIxFCE.d.mts.map} +1 -1
- package/dist/{types-DdkL6fyv.d.mts → types-BG8_XTLj.d.mts} +1 -1
- package/dist/{types-DdkL6fyv.d.mts.map → types-BG8_XTLj.d.mts.map} +1 -1
- package/dist/{types-BTnnBYVX.d.mts → types-BWh2MjmA.d.mts} +15 -2
- package/dist/types-BWh2MjmA.d.mts.map +1 -0
- package/dist/{types-CkEuk-Zr.d.mts → types-BWqGVhFn.d.mts} +1 -1
- package/dist/{types-CkEuk-Zr.d.mts.map → types-BWqGVhFn.d.mts.map} +1 -1
- package/dist/{types-DejCHqWT.mjs → types-CQAugunJ.mjs} +1 -1
- package/dist/{types-DejCHqWT.mjs.map → types-CQAugunJ.mjs.map} +1 -1
- package/dist/{types-Bzfk2yC8.d.mts → types-CTLJYMhN.d.mts} +1 -1
- package/dist/{types-Bzfk2yC8.d.mts.map → types-CTLJYMhN.d.mts.map} +1 -1
- package/dist/{types-BFgYtuKd.d.mts → types-CUsXGRVj.d.mts} +1 -1
- package/dist/{types-BFgYtuKd.d.mts.map → types-CUsXGRVj.d.mts.map} +1 -1
- package/dist/{types-BXSUSAjt.mjs → types-DXOIbece.mjs} +3 -3
- package/dist/types-DXOIbece.mjs.map +1 -0
- package/dist/{types-u_XxjbS8.d.mts → types-DrYMm9s5.d.mts} +1 -1
- package/dist/{types-u_XxjbS8.d.mts.map → types-DrYMm9s5.d.mts.map} +1 -1
- package/dist/{types-DO7whVYU.d.mts → types-TTx8uP7V.d.mts} +2 -2
- package/dist/{types-DO7whVYU.d.mts.map → types-TTx8uP7V.d.mts.map} +1 -1
- package/dist/types-fS7-VTdR.d.mts +124 -0
- package/dist/types-fS7-VTdR.d.mts.map +1 -0
- 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-B7A57fm9.mjs} +2 -2
- package/dist/{utils-C4M981Br.mjs.map → utils-B7A57fm9.mjs.map} +1 -1
- package/dist/{validate-DGhQPXzI.mjs → validate-0Utjspbs.mjs} +3 -3
- package/dist/{validate-DGhQPXzI.mjs.map → validate-0Utjspbs.mjs.map} +1 -1
- package/dist/{validate-cJOiOvT2.d.mts → validate-DOFrIb-c.d.mts} +5 -5
- package/dist/{validate-cJOiOvT2.d.mts.map → validate-DOFrIb-c.d.mts.map} +1 -1
- package/dist/{validation-DVHjPM1M.mjs → validation-D9EnzH1T.mjs} +6 -6
- package/dist/{validation-DVHjPM1M.mjs.map → validation-D9EnzH1T.mjs.map} +1 -1
- package/dist/version-BgVM_1MU.mjs +7 -0
- package/dist/{version-BOjj_cfz.mjs.map → version-BgVM_1MU.mjs.map} +1 -1
- package/dist/{widgets-Ci6hLwfO.mjs → widgets-BSTzPUYa.mjs} +4 -4
- package/dist/{widgets-Ci6hLwfO.mjs.map → widgets-BSTzPUYa.mjs.map} +1 -1
- package/dist/{zod-generator-CarzgPAu.mjs → zod-generator-ElVQOav7.mjs} +51 -15
- package/dist/zod-generator-ElVQOav7.mjs.map +1 -0
- package/package.json +19 -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/bylines.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 +40 -0
- package/src/astro/integration/vite-config.ts +25 -1
- package/src/astro/middleware/auth.ts +4 -3
- package/src/astro/middleware/request-context.ts +6 -1
- package/src/astro/middleware/scoped-db.ts +131 -0
- package/src/astro/middleware/stream-end-metrics.ts +15 -9
- package/src/astro/middleware.ts +131 -31
- package/src/astro/object-cache/adapters.ts +50 -0
- package/src/astro/prefetch.ts +77 -0
- package/src/astro/routes/admin.astro +5 -1
- package/src/astro/routes/api/comments/[collection]/[contentId]/reactions.ts +97 -0
- package/src/astro/routes/api/schema/index.ts +9 -3
- 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/cli/commands/export-seed.ts +10 -7
- package/src/comments/query.ts +78 -7
- package/src/comments/ranking.ts +58 -0
- package/src/components/Block.astro +71 -0
- package/src/components/Comments.astro +136 -1
- package/src/components/EmDashMedia.astro +1 -2
- package/src/components/Image.astro +36 -2
- package/src/components/InlinePortableTextEditor.tsx +10 -2
- package/src/components/index.ts +5 -0
- package/src/components/portable-text-text-align.ts +38 -0
- package/src/content/converters/portable-text-to-prosemirror.ts +5 -1
- package/src/content/converters/prosemirror-to-portable-text.ts +8 -0
- package/src/content/converters/types.ts +1 -0
- 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 +27 -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/repositories/types.ts +13 -0
- package/src/database/types.ts +9 -0
- package/src/emdash-runtime.ts +244 -75
- package/src/index.ts +22 -0
- package/src/loader.ts +97 -5
- package/src/mcp/server.ts +307 -20
- package/src/media/image-endpoint.ts +167 -0
- package/src/media/local-runtime.ts +18 -5
- 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/plugins/context.ts +94 -22
- package/src/plugins/cron.ts +18 -2
- package/src/plugins/routes.ts +40 -1
- package/src/query.ts +304 -18
- package/src/schema/query.ts +11 -4
- package/src/schema/zod-generator.ts +53 -8
- package/src/settings/index.ts +18 -5
- package/src/taxonomies/index.ts +263 -165
- package/src/ui.ts +5 -0
- package/src/virtual-modules.d.ts +24 -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/context-Cm4pt1Ws.mjs.map +0 -1
- package/dist/cron-DdEVrQ2Y.mjs.map +0 -1
- package/dist/index-D2VAiumu.d.mts.map +0 -1
- package/dist/init-lock-DlBHjf9-.mjs.map +0 -1
- package/dist/loader-BqWjcH3h.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/types-BTnnBYVX.d.mts.map +0 -1
- package/dist/types-BXSUSAjt.mjs.map +0 -1
- package/dist/version-BOjj_cfz.mjs +0 -7
- package/dist/zod-generator-CarzgPAu.mjs.map +0 -1
- /package/dist/{api-tokens-C7ywRx7l.mjs → api-tokens-B4BQybOp.mjs} +0 -0
- /package/dist/{ssrf-CRZGzjdL.mjs → ssrf-CcX9zvMK.mjs} +0 -0
- /package/dist/{types-BoRm8-pp.mjs → types-BvbeGEtr.mjs} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context-Cm4pt1Ws.mjs","names":[],"sources":["../src/plugins/storage-query.ts","../src/database/repositories/plugin-storage.ts","../src/plugins/context.ts"],"sourcesContent":["/**\n * Plugin Storage Query Validation and Building\n *\n * Validates that queries only use indexed fields and builds SQL WHERE clauses.\n *\n * @see PLUGIN-SYSTEM.md § Plugin Storage > Query Validation\n */\n\nimport type { Kysely } from \"kysely\";\n\nimport { jsonExtractExpr } from \"../database/dialect-helpers.js\";\nimport { validateJsonFieldName } from \"../database/validate.js\";\nimport type { WhereClause, WhereValue, RangeFilter, InFilter, StartsWithFilter } from \"./types.js\";\n\n/**\n * Error thrown when querying non-indexed fields\n */\nexport class StorageQueryError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic field?: string,\n\t\tpublic suggestion?: string,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"StorageQueryError\";\n\t}\n}\n\n/**\n * Check if a value is a range filter\n */\nexport function isRangeFilter(value: WhereValue): value is RangeFilter {\n\tif (typeof value !== \"object\" || value === null) return false;\n\treturn \"gt\" in value || \"gte\" in value || \"lt\" in value || \"lte\" in value;\n}\n\n/**\n * Check if a value is an IN filter\n */\nexport function isInFilter(value: WhereValue): value is InFilter {\n\tif (typeof value !== \"object\" || value === null) return false;\n\treturn \"in\" in value && Array.isArray(value.in);\n}\n\n/**\n * Check if a value is a startsWith filter\n */\nexport function isStartsWithFilter(value: WhereValue): value is StartsWithFilter {\n\tif (typeof value !== \"object\" || value === null) return false;\n\treturn \"startsWith\" in value && typeof value.startsWith === \"string\";\n}\n\n/**\n * Get the set of indexed fields from index declarations\n */\nexport function getIndexedFields(indexes: Array<string | string[]>): Set<string> {\n\tconst fields = new Set<string>();\n\tfor (const index of indexes) {\n\t\tif (Array.isArray(index)) {\n\t\t\tfor (const field of index) {\n\t\t\t\tfields.add(field);\n\t\t\t}\n\t\t} else {\n\t\t\tfields.add(index);\n\t\t}\n\t}\n\treturn fields;\n}\n\n/**\n * Validate that all fields in a where clause are indexed\n */\nexport function validateWhereClause(\n\twhere: WhereClause,\n\tindexedFields: Set<string>,\n\tpluginId: string,\n\tcollection: string,\n): void {\n\tfor (const field of Object.keys(where)) {\n\t\tif (!indexedFields.has(field)) {\n\t\t\tthrow new StorageQueryError(\n\t\t\t\t`Cannot query on non-indexed field '${field}'.`,\n\t\t\t\tfield,\n\t\t\t\t`Add '${field}' to storage.${collection}.indexes in plugin '${pluginId}' to enable this query.`,\n\t\t\t);\n\t\t}\n\t}\n}\n\n/**\n * Validate orderBy fields are indexed\n */\nexport function validateOrderByClause(\n\torderBy: Record<string, \"asc\" | \"desc\">,\n\tindexedFields: Set<string>,\n\tpluginId: string,\n\tcollection: string,\n): void {\n\tfor (const field of Object.keys(orderBy)) {\n\t\tif (!indexedFields.has(field)) {\n\t\t\tthrow new StorageQueryError(\n\t\t\t\t`Cannot order by non-indexed field '${field}'.`,\n\t\t\t\tfield,\n\t\t\t\t`Add '${field}' to storage.${collection}.indexes in plugin '${pluginId}' to enable ordering by this field.`,\n\t\t\t);\n\t\t}\n\t}\n}\n\n/**\n * SQL expression for extracting JSON field.\n *\n * Validates the field name before interpolation to prevent SQL injection\n * via crafted JSON path expressions.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport function jsonExtract(db: Kysely<any>, field: string): string {\n\tvalidateJsonFieldName(field, \"query field name\");\n\treturn jsonExtractExpr(db, \"data\", field);\n}\n\n/**\n * Build a WHERE clause condition for a single field\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport function buildCondition(\n\tdb: Kysely<any>,\n\tfield: string,\n\tvalue: WhereValue,\n): { sql: string; params: unknown[] } {\n\tconst extract = jsonExtract(db, field);\n\n\tif (value === null) {\n\t\treturn { sql: `${extract} IS NULL`, params: [] };\n\t}\n\n\tif (typeof value === \"string\" || typeof value === \"number\") {\n\t\treturn { sql: `${extract} = ?`, params: [value] };\n\t}\n\n\tif (typeof value === \"boolean\") {\n\t\t// JSON booleans are stored as true/false strings\n\t\treturn { sql: `${extract} = ?`, params: [value] };\n\t}\n\n\tif (isInFilter(value)) {\n\t\tconst placeholders = value.in.map(() => \"?\").join(\", \");\n\t\treturn {\n\t\t\tsql: `${extract} IN (${placeholders})`,\n\t\t\tparams: value.in,\n\t\t};\n\t}\n\n\tif (isStartsWithFilter(value)) {\n\t\treturn {\n\t\t\tsql: `${extract} LIKE ?`,\n\t\t\tparams: [`${value.startsWith}%`],\n\t\t};\n\t}\n\n\tif (isRangeFilter(value)) {\n\t\tconst conditions: string[] = [];\n\t\tconst params: unknown[] = [];\n\n\t\tif (value.gt !== undefined) {\n\t\t\tconditions.push(`${extract} > ?`);\n\t\t\tparams.push(value.gt);\n\t\t}\n\t\tif (value.gte !== undefined) {\n\t\t\tconditions.push(`${extract} >= ?`);\n\t\t\tparams.push(value.gte);\n\t\t}\n\t\tif (value.lt !== undefined) {\n\t\t\tconditions.push(`${extract} < ?`);\n\t\t\tparams.push(value.lt);\n\t\t}\n\t\tif (value.lte !== undefined) {\n\t\t\tconditions.push(`${extract} <= ?`);\n\t\t\tparams.push(value.lte);\n\t\t}\n\n\t\treturn {\n\t\t\tsql: conditions.join(\" AND \"),\n\t\t\tparams,\n\t\t};\n\t}\n\n\tthrow new StorageQueryError(`Unknown filter type for field '${field}'`);\n}\n\n/**\n * Build a complete WHERE clause from a WhereClause object\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport function buildWhereClause(\n\tdb: Kysely<any>,\n\twhere: WhereClause,\n): {\n\tsql: string;\n\tparams: unknown[];\n} {\n\tconst conditions: string[] = [];\n\tconst params: unknown[] = [];\n\n\tfor (const [field, value] of Object.entries(where)) {\n\t\tconst condition = buildCondition(db, field, value);\n\t\tconditions.push(condition.sql);\n\t\tparams.push(...condition.params);\n\t}\n\n\tif (conditions.length === 0) {\n\t\treturn { sql: \"\", params: [] };\n\t}\n\n\treturn {\n\t\tsql: conditions.join(\" AND \"),\n\t\tparams,\n\t};\n}\n\n/**\n * Build ORDER BY clause\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport function buildOrderByClause(\n\tdb: Kysely<any>,\n\torderBy: Record<string, \"asc\" | \"desc\">,\n): string {\n\tconst clauses: string[] = [];\n\n\tfor (const [field, direction] of Object.entries(orderBy)) {\n\t\tclauses.push(`${jsonExtract(db, field)} ${direction.toUpperCase()}`);\n\t}\n\n\tif (clauses.length === 0) {\n\t\treturn \"\";\n\t}\n\n\treturn `ORDER BY ${clauses.join(\", \")}`;\n}\n","/**\n * Plugin Storage Repository\n *\n * Provides a document store API for plugin data storage.\n * Uses a single _plugin_storage table with JSON documents and expression indexes.\n *\n * @see PLUGIN-SYSTEM.md § Plugin Storage > Full API Reference\n */\n\nimport type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport {\n\tbuildWhereClause,\n\tvalidateWhereClause,\n\tvalidateOrderByClause,\n\tgetIndexedFields,\n\tjsonExtract,\n} from \"../../plugins/storage-query.js\";\nimport type {\n\tStorageCollection,\n\tQueryOptions,\n\tPaginatedResult,\n\tWhereClause,\n} from \"../../plugins/types.js\";\nimport { withTransaction } from \"../transaction.js\";\nimport type { Database } from \"../types.js\";\nimport { encodeCursor, decodeCursor } from \"./types.js\";\n\n/**\n * Plugin Storage Repository\n *\n * Implements the StorageCollection interface for a specific plugin and collection.\n */\nexport class PluginStorageRepository<T = unknown> implements StorageCollection<T> {\n\tprivate indexedFields: Set<string>;\n\n\tconstructor(\n\t\tprivate db: Kysely<Database>,\n\t\tprivate pluginId: string,\n\t\tprivate collection: string,\n\t\tindexes: Array<string | string[]>,\n\t) {\n\t\tthis.indexedFields = getIndexedFields(indexes);\n\t}\n\n\t/**\n\t * Get a document by ID\n\t */\n\tasync get(id: string): Promise<T | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_plugin_storage\")\n\t\t\t.select(\"data\")\n\t\t\t.where(\"plugin_id\", \"=\", this.pluginId)\n\t\t\t.where(\"collection\", \"=\", this.collection)\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\n\t\tif (!row) return null;\n\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- JSON.parse returns any; generic callers provide T\n\t\treturn JSON.parse(row.data) as T;\n\t}\n\n\t/**\n\t * Store a document\n\t */\n\tasync put(id: string, data: T): Promise<void> {\n\t\tconst now = new Date().toISOString();\n\t\tconst jsonData = JSON.stringify(data);\n\n\t\tawait this.db\n\t\t\t.insertInto(\"_plugin_storage\")\n\t\t\t.values({\n\t\t\t\tplugin_id: this.pluginId,\n\t\t\t\tcollection: this.collection,\n\t\t\t\tid,\n\t\t\t\tdata: jsonData,\n\t\t\t\tcreated_at: now,\n\t\t\t\tupdated_at: now,\n\t\t\t})\n\t\t\t.onConflict((oc) =>\n\t\t\t\toc.columns([\"plugin_id\", \"collection\", \"id\"]).doUpdateSet({\n\t\t\t\t\tdata: jsonData,\n\t\t\t\t\tupdated_at: now,\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.execute();\n\t}\n\n\t/**\n\t * Delete a document\n\t */\n\tasync delete(id: string): Promise<boolean> {\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"_plugin_storage\")\n\t\t\t.where(\"plugin_id\", \"=\", this.pluginId)\n\t\t\t.where(\"collection\", \"=\", this.collection)\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\n\t\treturn (result.numDeletedRows ?? 0) > 0;\n\t}\n\n\t/**\n\t * Check if a document exists\n\t */\n\tasync exists(id: string): Promise<boolean> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_plugin_storage\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"plugin_id\", \"=\", this.pluginId)\n\t\t\t.where(\"collection\", \"=\", this.collection)\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\n\t\treturn !!row;\n\t}\n\n\t/**\n\t * Get multiple documents by ID\n\t */\n\tasync getMany(ids: string[]): Promise<Map<string, T>> {\n\t\tif (ids.length === 0) return new Map();\n\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_plugin_storage\")\n\t\t\t.select([\"id\", \"data\"])\n\t\t\t.where(\"plugin_id\", \"=\", this.pluginId)\n\t\t\t.where(\"collection\", \"=\", this.collection)\n\t\t\t.where(\"id\", \"in\", ids)\n\t\t\t.execute();\n\n\t\tconst result = new Map<string, T>();\n\t\tfor (const row of rows) {\n\t\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- JSON.parse returns any; generic callers provide T\n\t\t\tresult.set(row.id, JSON.parse(row.data) as T);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Store multiple documents\n\t */\n\tasync putMany(items: Array<{ id: string; data: T }>): Promise<void> {\n\t\tif (items.length === 0) return;\n\n\t\tconst now = new Date().toISOString();\n\n\t\t// SQLite doesn't support batch upserts well, so we do them one at a time\n\t\t// In a transaction for atomicity\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tfor (const item of items) {\n\t\t\t\tconst jsonData = JSON.stringify(item.data);\n\t\t\t\tawait trx\n\t\t\t\t\t.insertInto(\"_plugin_storage\")\n\t\t\t\t\t.values({\n\t\t\t\t\t\tplugin_id: this.pluginId,\n\t\t\t\t\t\tcollection: this.collection,\n\t\t\t\t\t\tid: item.id,\n\t\t\t\t\t\tdata: jsonData,\n\t\t\t\t\t\tcreated_at: now,\n\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t})\n\t\t\t\t\t.onConflict((oc) =>\n\t\t\t\t\t\toc.columns([\"plugin_id\", \"collection\", \"id\"]).doUpdateSet({\n\t\t\t\t\t\t\tdata: jsonData,\n\t\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t\t.execute();\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Delete multiple documents\n\t */\n\tasync deleteMany(ids: string[]): Promise<number> {\n\t\tif (ids.length === 0) return 0;\n\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"_plugin_storage\")\n\t\t\t.where(\"plugin_id\", \"=\", this.pluginId)\n\t\t\t.where(\"collection\", \"=\", this.collection)\n\t\t\t.where(\"id\", \"in\", ids)\n\t\t\t.executeTakeFirst();\n\n\t\treturn Number(result.numDeletedRows ?? 0);\n\t}\n\n\t/**\n\t * Query documents with filters\n\t */\n\tasync query(options: QueryOptions = {}): Promise<PaginatedResult<{ id: string; data: T }>> {\n\t\tconst { where = {}, orderBy = {}, cursor } = options;\n\t\tconst limit = Math.min(options.limit ?? 50, 100);\n\n\t\t// Validate that all queried fields are indexed\n\t\tvalidateWhereClause(where, this.indexedFields, this.pluginId, this.collection);\n\t\tif (Object.keys(orderBy).length > 0) {\n\t\t\tvalidateOrderByClause(orderBy, this.indexedFields, this.pluginId, this.collection);\n\t\t}\n\n\t\t// Build base query\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_plugin_storage\")\n\t\t\t.select([\"id\", \"data\", \"created_at\"])\n\t\t\t.where(\"plugin_id\", \"=\", this.pluginId)\n\t\t\t.where(\"collection\", \"=\", this.collection);\n\n\t\t// Add JSON extraction WHERE conditions\n\t\tconst whereResult = buildWhereClause(this.db, where);\n\t\tif (whereResult.sql) {\n\t\t\t// Use sql template to add the raw WHERE conditions with params\n\t\t\tconst whereSqlParts: ReturnType<typeof sql>[] = [];\n\t\t\tlet paramIndex = 0;\n\t\t\tconst sqlParts = whereResult.sql.split(\"?\");\n\t\t\tfor (let i = 0; i < sqlParts.length; i++) {\n\t\t\t\tif (i > 0) {\n\t\t\t\t\twhereSqlParts.push(sql`${whereResult.params[paramIndex++]}`);\n\t\t\t\t}\n\t\t\t\tif (sqlParts[i]) {\n\t\t\t\t\twhereSqlParts.push(sql.raw(sqlParts[i]));\n\t\t\t\t}\n\t\t\t}\n\t\t\tquery = query.where(({ eb }) => eb(sql.join(whereSqlParts, sql.raw(\"\")), \"=\", sql.raw(\"1\")));\n\t\t}\n\n\t\t// Handle cursor-based pagination — throws on invalid cursor.\n\t\tif (cursor) {\n\t\t\tconst decoded = decodeCursor(cursor);\n\t\t\tquery = query.where(({ eb }) =>\n\t\t\t\teb(sql`(created_at, id)`, \">\", sql`(${decoded.orderValue}, ${decoded.id})`),\n\t\t\t);\n\t\t}\n\n\t\t// Build ORDER BY using sql template\n\t\tif (Object.keys(orderBy).length > 0) {\n\t\t\tfor (const [field, direction] of Object.entries(orderBy)) {\n\t\t\t\tconst extract = jsonExtract(this.db, field);\n\t\t\t\tconst orderExpr =\n\t\t\t\t\tdirection === \"desc\" ? sql`${sql.raw(extract)} desc` : sql`${sql.raw(extract)} asc`;\n\t\t\t\tquery = query.orderBy(orderExpr);\n\t\t\t}\n\t\t} else {\n\t\t\t// Default ordering for consistent pagination\n\t\t\tquery = query.orderBy(\"created_at\", \"asc\").orderBy(\"id\", \"asc\");\n\t\t}\n\n\t\t// Apply limit (fetch one extra to detect if there's more)\n\t\tquery = query.limit(limit + 1);\n\n\t\tconst rows = await query.execute();\n\n\t\tconst hasMore = rows.length > limit;\n\t\tconst items = rows.slice(0, limit).map((row) => ({\n\t\t\tid: row.id,\n\t\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- JSON.parse returns any; generic callers provide T\n\t\t\tdata: JSON.parse(row.data) as T,\n\t\t}));\n\n\t\t// Generate cursor for next page if there are more results\n\t\tlet nextCursor: string | undefined;\n\t\tif (hasMore) {\n\t\t\tconst lastItem = rows[limit - 1];\n\t\t\tif (lastItem) {\n\t\t\t\tnextCursor = encodeCursor(lastItem.created_at, lastItem.id);\n\t\t\t}\n\t\t}\n\n\t\treturn { items, cursor: nextCursor, hasMore };\n\t}\n\n\t/**\n\t * Count documents matching a filter\n\t */\n\tasync count(where?: WhereClause): Promise<number> {\n\t\tif (where && Object.keys(where).length > 0) {\n\t\t\tvalidateWhereClause(where, this.indexedFields, this.pluginId, this.collection);\n\t\t}\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_plugin_storage\")\n\t\t\t.select(sql<number>`COUNT(*)`.as(\"count\"))\n\t\t\t.where(\"plugin_id\", \"=\", this.pluginId)\n\t\t\t.where(\"collection\", \"=\", this.collection);\n\n\t\t// Add JSON extraction WHERE conditions\n\t\tif (where && Object.keys(where).length > 0) {\n\t\t\tconst whereResult = buildWhereClause(this.db, where);\n\t\t\tif (whereResult.sql) {\n\t\t\t\t// Use sql template to add the raw WHERE conditions with params\n\t\t\t\tconst whereSqlParts: ReturnType<typeof sql>[] = [];\n\t\t\t\tlet paramIndex = 0;\n\t\t\t\tconst sqlParts = whereResult.sql.split(\"?\");\n\t\t\t\tfor (let i = 0; i < sqlParts.length; i++) {\n\t\t\t\t\tif (i > 0) {\n\t\t\t\t\t\twhereSqlParts.push(sql`${whereResult.params[paramIndex++]}`);\n\t\t\t\t\t}\n\t\t\t\t\tif (sqlParts[i]) {\n\t\t\t\t\t\twhereSqlParts.push(sql.raw(sqlParts[i]));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tquery = query.where(({ eb }) =>\n\t\t\t\t\teb(sql.join(whereSqlParts, sql.raw(\"\")), \"=\", sql.raw(\"1\")),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst result = await query.executeTakeFirst();\n\t\treturn result?.count ?? 0;\n\t}\n}\n\n/**\n * Create a scoped storage accessor for a plugin\n */\nexport function createPluginStorageAccessor(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n\tstorageConfig: Record<\n\t\tstring,\n\t\t{ indexes: Array<string | string[]>; uniqueIndexes?: Array<string | string[]> }\n\t>,\n): Record<string, StorageCollection> {\n\tconst accessor: Record<string, StorageCollection> = {};\n\n\tfor (const [collectionName, config] of Object.entries(storageConfig)) {\n\t\tconst allIndexes = [...config.indexes, ...(config.uniqueIndexes ?? [])];\n\t\taccessor[collectionName] = new PluginStorageRepository(\n\t\t\tdb,\n\t\t\tpluginId,\n\t\t\tcollectionName,\n\t\t\tallIndexes,\n\t\t);\n\t}\n\n\treturn accessor;\n}\n\n/**\n * Delete all storage data for a plugin\n */\nexport async function deleteAllPluginStorage(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n): Promise<number> {\n\tconst result = await db\n\t\t.deleteFrom(\"_plugin_storage\")\n\t\t.where(\"plugin_id\", \"=\", pluginId)\n\t\t.executeTakeFirst();\n\n\treturn Number(result.numDeletedRows ?? 0);\n}\n\n/**\n * Delete all storage data for a plugin collection\n */\nexport async function deletePluginCollection(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n\tcollection: string,\n): Promise<number> {\n\tconst result = await db\n\t\t.deleteFrom(\"_plugin_storage\")\n\t\t.where(\"plugin_id\", \"=\", pluginId)\n\t\t.where(\"collection\", \"=\", collection)\n\t\t.executeTakeFirst();\n\n\treturn Number(result.numDeletedRows ?? 0);\n}\n","/**\n * Plugin Context v2\n *\n * Creates the unified context object provided to plugins in all hooks and routes.\n *\n */\n\nimport type { Kysely } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { ContentRepository } from \"../database/repositories/content.js\";\nimport { MediaRepository } from \"../database/repositories/media.js\";\nimport { OptionsRepository } from \"../database/repositories/options.js\";\nimport { PluginStorageRepository } from \"../database/repositories/plugin-storage.js\";\nimport { SeoRepository } from \"../database/repositories/seo.js\";\nimport { UserRepository } from \"../database/repositories/user.js\";\nimport { withTransaction } from \"../database/transaction.js\";\nimport type { Database } from \"../database/types.js\";\nimport {\n\tresolveAndValidateExternalUrl,\n\tSsrfError,\n\tstripCredentialHeaders,\n} from \"../import/ssrf.js\";\nimport { invalidateSiteSettingsCache } from \"../settings/index.js\";\nimport type { Storage } from \"../storage/types.js\";\nimport { CronAccessImpl } from \"./cron.js\";\nimport type { EmailPipeline } from \"./email.js\";\nimport type {\n\tResolvedPlugin,\n\tPluginContext,\n\tPluginStorageConfig,\n\tStorageCollection,\n\tKVAccess,\n\tCronAccess,\n\tEmailAccess,\n\tContentAccess,\n\tContentAccessWithWrite,\n\tMediaAccess,\n\tMediaAccessWithWrite,\n\tHttpAccess,\n\tLogAccess,\n\tSiteInfo,\n\tUserAccess,\n\tUserInfo,\n\tContentItem,\n\tContentItemSeoInput,\n\tContentWriteInput,\n\tMediaItem,\n\tPaginatedResult,\n\tQueryOptions,\n\tContentListOptions,\n\tMediaListOptions,\n} from \"./types.js\";\n\n// =============================================================================\n// KV Access\n// =============================================================================\n\n/**\n * Create KV accessor for a plugin\n * All keys are automatically prefixed with the plugin ID\n */\nexport function createKVAccess(optionsRepo: OptionsRepository, pluginId: string): KVAccess {\n\tconst prefix = `plugin:${pluginId}:`;\n\n\treturn {\n\t\tasync get<T>(key: string): Promise<T | null> {\n\t\t\treturn optionsRepo.get<T>(`${prefix}${key}`);\n\t\t},\n\n\t\tasync set(key: string, value: unknown): Promise<void> {\n\t\t\tawait optionsRepo.set(`${prefix}${key}`, value);\n\t\t},\n\n\t\tasync delete(key: string): Promise<boolean> {\n\t\t\treturn optionsRepo.delete(`${prefix}${key}`);\n\t\t},\n\n\t\tasync list(keyPrefix?: string): Promise<Array<{ key: string; value: unknown }>> {\n\t\t\tconst fullPrefix = `${prefix}${keyPrefix ?? \"\"}`;\n\t\t\tconst entriesMap = await optionsRepo.getByPrefix(fullPrefix);\n\t\t\tconst result: Array<{ key: string; value: unknown }> = [];\n\t\t\tfor (const [fullKey, value] of entriesMap) {\n\t\t\t\tresult.push({\n\t\t\t\t\tkey: fullKey.slice(prefix.length),\n\t\t\t\t\tvalue,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn result;\n\t\t},\n\t};\n}\n\n// =============================================================================\n// Storage Access\n// =============================================================================\n\n/**\n * Create storage collection accessor for a plugin\n * Wraps PluginStorageRepository with the v2 interface (no async iterators)\n */\nfunction createStorageCollection<T>(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n\tcollectionName: string,\n\tindexes: Array<string | string[]>,\n): StorageCollection<T> {\n\tconst repo = new PluginStorageRepository<T>(db, pluginId, collectionName, indexes);\n\n\treturn {\n\t\tget: (id) => repo.get(id),\n\t\tput: (id, data) => repo.put(id, data),\n\t\tdelete: (id) => repo.delete(id),\n\t\texists: (id) => repo.exists(id),\n\t\tgetMany: (ids) => repo.getMany(ids),\n\t\tputMany: (items) => repo.putMany(items),\n\t\tdeleteMany: (ids) => repo.deleteMany(ids),\n\t\tcount: (where) => repo.count(where),\n\n\t\t// Query returns PaginatedResult instead of the old format\n\t\tasync query(options?: QueryOptions): Promise<PaginatedResult<{ id: string; data: T }>> {\n\t\t\tconst result = await repo.query({\n\t\t\t\twhere: options?.where,\n\t\t\t\torderBy: options?.orderBy,\n\t\t\t\tlimit: options?.limit,\n\t\t\t\tcursor: options?.cursor,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\titems: result.items,\n\t\t\t\tcursor: result.cursor,\n\t\t\t\thasMore: result.hasMore,\n\t\t\t};\n\t\t},\n\t};\n}\n\n/**\n * Create storage accessor with all declared collections\n */\nexport function createStorageAccess<T extends PluginStorageConfig>(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n\tstorageConfig: T,\n): Record<string, StorageCollection> {\n\tconst storage: Record<string, StorageCollection> = {};\n\n\tfor (const [collectionName, config] of Object.entries(storageConfig)) {\n\t\tconst allIndexes = [...config.indexes, ...(config.uniqueIndexes ?? [])];\n\t\tstorage[collectionName] = createStorageCollection(db, pluginId, collectionName, allIndexes);\n\t}\n\n\treturn storage;\n}\n\n// =============================================================================\n// Content Access\n// =============================================================================\n\n/**\n * Extract `seo` from a plugin-supplied content write input and return both\n * parts. Mutates nothing — returns a new field map without the `seo` key.\n */\nfunction splitSeoFromInput(input: ContentWriteInput): {\n\tfields: Record<string, unknown>;\n\tseo: ContentItemSeoInput | undefined;\n} {\n\tconst { seo, ...fields } = input;\n\t// Reject non-object seo values rather than silently dropping them.\n\tif (seo !== undefined && (seo === null || typeof seo !== \"object\" || Array.isArray(seo))) {\n\t\tthrow new Error(\"content.seo must be an object\");\n\t}\n\treturn { fields, seo };\n}\n\n/**\n * Reject writing SEO to a collection that does not have it enabled.\n * Matches the REST API behavior (VALIDATION_ERROR).\n */\nasync function assertSeoEnabled(\n\tseoRepo: SeoRepository,\n\tcollection: string,\n\tseo: ContentItemSeoInput | undefined,\n): Promise<boolean> {\n\tconst hasSeo = await seoRepo.isEnabled(collection);\n\tif (seo !== undefined && !hasSeo) {\n\t\tthrow new Error(\n\t\t\t`Collection \"${collection}\" does not have SEO enabled. ` +\n\t\t\t\t`Remove the seo field or enable SEO on this collection.`,\n\t\t);\n\t}\n\treturn hasSeo;\n}\n\n/**\n * Create read-only content access\n */\nexport function createContentAccess(db: Kysely<Database>): ContentAccess {\n\tconst contentRepo = new ContentRepository(db);\n\tconst seoRepo = new SeoRepository(db);\n\n\treturn {\n\t\tasync get(collection: string, id: string): Promise<ContentItem | null> {\n\t\t\tconst item = await contentRepo.findById(collection, id);\n\t\t\tif (!item) return null;\n\n\t\t\tconst result: ContentItem = {\n\t\t\t\tid: item.id,\n\t\t\t\ttype: item.type,\n\t\t\t\tslug: item.slug,\n\t\t\t\tstatus: item.status,\n\t\t\t\tdata: item.data,\n\t\t\t\tcreatedAt: item.createdAt,\n\t\t\t\tupdatedAt: item.updatedAt,\n\t\t\t\tlocale: item.locale,\n\t\t\t\tpublishedAt: item.publishedAt,\n\t\t\t};\n\n\t\t\tif (await seoRepo.isEnabled(collection)) {\n\t\t\t\tresult.seo = await seoRepo.get(collection, item.id);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t},\n\n\t\tasync list(\n\t\t\tcollection: string,\n\t\t\toptions?: ContentListOptions,\n\t\t): Promise<PaginatedResult<ContentItem>> {\n\t\t\t// Convert orderBy format if provided\n\t\t\tlet orderBy: { field: string; direction: \"asc\" | \"desc\" } | undefined;\n\t\t\tif (options?.orderBy) {\n\t\t\t\tconst entries = Object.entries(options.orderBy);\n\t\t\t\tconst first = entries[0];\n\t\t\t\tif (first) {\n\t\t\t\t\torderBy = { field: first[0], direction: first[1] };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst result = await contentRepo.findMany(collection, {\n\t\t\t\tlimit: options?.limit ?? 50,\n\t\t\t\tcursor: options?.cursor,\n\t\t\t\torderBy,\n\t\t\t\twhere: options?.where,\n\t\t\t});\n\n\t\t\tconst items: ContentItem[] = result.items.map((item) => ({\n\t\t\t\tid: item.id,\n\t\t\t\ttype: item.type,\n\t\t\t\tslug: item.slug,\n\t\t\t\tstatus: item.status,\n\t\t\t\tdata: item.data,\n\t\t\t\tcreatedAt: item.createdAt,\n\t\t\t\tupdatedAt: item.updatedAt,\n\t\t\t\tlocale: item.locale,\n\t\t\t\tpublishedAt: item.publishedAt,\n\t\t\t}));\n\n\t\t\tif (items.length > 0 && (await seoRepo.isEnabled(collection))) {\n\t\t\t\tconst seoMap = await seoRepo.getMany(\n\t\t\t\t\tcollection,\n\t\t\t\t\titems.map((i) => i.id),\n\t\t\t\t);\n\t\t\t\tfor (const item of items) {\n\t\t\t\t\tconst seo = seoMap.get(item.id);\n\t\t\t\t\tif (seo) item.seo = seo;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\titems,\n\t\t\t\tcursor: result.nextCursor,\n\t\t\t\thasMore: !!result.nextCursor,\n\t\t\t};\n\t\t},\n\t};\n}\n\n/**\n * Create full content access with write operations.\n *\n * `create` and `update` accept a reserved `seo` key in their `data`\n * argument. When present, it is routed to the core SEO panel\n * (`_emdash_seo`) via `SeoRepository.upsert`, in the same transaction as\n * the content write. The returned `ContentItem.seo` reflects the resulting\n * SEO state for SEO-enabled collections.\n */\nexport function createContentAccessWithWrite(db: Kysely<Database>): ContentAccessWithWrite {\n\tconst readAccess = createContentAccess(db);\n\n\treturn {\n\t\t...readAccess,\n\n\t\tasync create(collection: string, data: ContentWriteInput): Promise<ContentItem> {\n\t\t\tconst { fields, seo } = splitSeoFromInput(data);\n\n\t\t\treturn withTransaction(db, async (trx) => {\n\t\t\t\tconst trxContentRepo = new ContentRepository(trx);\n\t\t\t\tconst trxSeoRepo = new SeoRepository(trx);\n\n\t\t\t\tconst hasSeo = await assertSeoEnabled(trxSeoRepo, collection, seo);\n\n\t\t\t\tconst item = await trxContentRepo.create({\n\t\t\t\t\ttype: collection,\n\t\t\t\t\tdata: fields,\n\t\t\t\t});\n\n\t\t\t\tconst result: ContentItem = {\n\t\t\t\t\tid: item.id,\n\t\t\t\t\ttype: item.type,\n\t\t\t\t\tslug: item.slug,\n\t\t\t\t\tstatus: item.status,\n\t\t\t\t\tdata: item.data,\n\t\t\t\t\tcreatedAt: item.createdAt,\n\t\t\t\t\tupdatedAt: item.updatedAt,\n\t\t\t\t\tlocale: item.locale,\n\t\t\t\t\tpublishedAt: item.publishedAt,\n\t\t\t\t};\n\n\t\t\t\tif (hasSeo) {\n\t\t\t\t\tresult.seo =\n\t\t\t\t\t\tseo !== undefined\n\t\t\t\t\t\t\t? await trxSeoRepo.upsert(collection, item.id, seo)\n\t\t\t\t\t\t\t: await trxSeoRepo.get(collection, item.id);\n\t\t\t\t}\n\n\t\t\t\treturn result;\n\t\t\t});\n\t\t},\n\n\t\tasync update(collection: string, id: string, data: ContentWriteInput): Promise<ContentItem> {\n\t\t\tconst { fields, seo } = splitSeoFromInput(data);\n\n\t\t\treturn withTransaction(db, async (trx) => {\n\t\t\t\tconst trxContentRepo = new ContentRepository(trx);\n\t\t\t\tconst trxSeoRepo = new SeoRepository(trx);\n\n\t\t\t\tconst hasSeo = await assertSeoEnabled(trxSeoRepo, collection, seo);\n\n\t\t\t\t// Pass the `data` payload to ContentRepository.update only when\n\t\t\t\t// there are field updates — passing an empty object would still\n\t\t\t\t// bump updated_at/version, but we want a seo-only call to touch\n\t\t\t\t// only the SEO table. ContentRepository.update handles the no-op\n\t\t\t\t// path by returning the current row.\n\t\t\t\tconst hasFieldUpdates = Object.keys(fields).length > 0;\n\t\t\t\tconst item = hasFieldUpdates\n\t\t\t\t\t? await trxContentRepo.update(collection, id, { data: fields })\n\t\t\t\t\t: await (async () => {\n\t\t\t\t\t\t\tconst existing = await trxContentRepo.findById(collection, id);\n\t\t\t\t\t\t\tif (!existing) throw new Error(\"Content not found\");\n\t\t\t\t\t\t\treturn existing;\n\t\t\t\t\t\t})();\n\n\t\t\t\tconst result: ContentItem = {\n\t\t\t\t\tid: item.id,\n\t\t\t\t\ttype: item.type,\n\t\t\t\t\tslug: item.slug,\n\t\t\t\t\tstatus: item.status,\n\t\t\t\t\tdata: item.data,\n\t\t\t\t\tcreatedAt: item.createdAt,\n\t\t\t\t\tupdatedAt: item.updatedAt,\n\t\t\t\t\tlocale: item.locale,\n\t\t\t\t\tpublishedAt: item.publishedAt,\n\t\t\t\t};\n\n\t\t\t\tif (hasSeo) {\n\t\t\t\t\tresult.seo =\n\t\t\t\t\t\tseo !== undefined\n\t\t\t\t\t\t\t? await trxSeoRepo.upsert(collection, item.id, seo)\n\t\t\t\t\t\t\t: await trxSeoRepo.get(collection, item.id);\n\t\t\t\t}\n\n\t\t\t\treturn result;\n\t\t\t});\n\t\t},\n\n\t\tasync delete(collection: string, id: string): Promise<boolean> {\n\t\t\tconst contentRepo = new ContentRepository(db);\n\t\t\treturn contentRepo.delete(collection, id);\n\t\t},\n\t};\n}\n\n// =============================================================================\n// Media Access\n// =============================================================================\n\n/**\n * Create read-only media access\n */\nexport function createMediaAccess(db: Kysely<Database>): MediaAccess {\n\tconst mediaRepo = new MediaRepository(db);\n\n\treturn {\n\t\tasync get(id: string): Promise<MediaItem | null> {\n\t\t\tconst item = await mediaRepo.findById(id);\n\t\t\tif (!item) return null;\n\n\t\t\treturn {\n\t\t\t\tid: item.id,\n\t\t\t\tfilename: item.filename,\n\t\t\t\tmimeType: item.mimeType,\n\t\t\t\tsize: item.size,\n\t\t\t\t// Construct URL from storage key (or use a sensible default path)\n\t\t\t\turl: `/media/${item.id}/${item.filename}`,\n\t\t\t\tcreatedAt: item.createdAt,\n\t\t\t};\n\t\t},\n\n\t\tasync list(options?: MediaListOptions): Promise<PaginatedResult<MediaItem>> {\n\t\t\tconst result = await mediaRepo.findMany({\n\t\t\t\tlimit: options?.limit ?? 50,\n\t\t\t\tcursor: options?.cursor,\n\t\t\t\tmimeType: options?.mimeType,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\titems: result.items.map((item) => ({\n\t\t\t\t\tid: item.id,\n\t\t\t\t\tfilename: item.filename,\n\t\t\t\t\tmimeType: item.mimeType,\n\t\t\t\t\tsize: item.size,\n\t\t\t\t\turl: `/media/${item.id}/${item.filename}`,\n\t\t\t\t\tcreatedAt: item.createdAt,\n\t\t\t\t})),\n\t\t\t\tcursor: result.nextCursor,\n\t\t\t\thasMore: !!result.nextCursor,\n\t\t\t};\n\t\t},\n\t};\n}\n\n/**\n * Create full media access with write operations.\n * If storage is not provided, upload() will throw at call time.\n */\nexport function createMediaAccessWithWrite(\n\tdb: Kysely<Database>,\n\tgetUploadUrlFn: (\n\t\tfilename: string,\n\t\tcontentType: string,\n\t) => Promise<{ uploadUrl: string; mediaId: string }>,\n\tstorage?: Storage,\n): MediaAccessWithWrite {\n\tconst mediaRepo = new MediaRepository(db);\n\tconst readAccess = createMediaAccess(db);\n\n\treturn {\n\t\t...readAccess,\n\n\t\tgetUploadUrl: getUploadUrlFn,\n\n\t\tasync upload(\n\t\t\tfilename: string,\n\t\t\tcontentType: string,\n\t\t\tbytes: ArrayBuffer,\n\t\t): Promise<{ mediaId: string; storageKey: string; url: string }> {\n\t\t\tif (!storage) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Media upload() requires a storage backend. Configure storage in PluginContextFactoryOptions.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Generate a storage key with a unique prefix\n\t\t\tconst keyPrefix = ulid();\n\t\t\t// Extract extension from basename (ignore path separators)\n\t\t\tconst basename = filename.split(\"/\").pop() ?? filename;\n\t\t\tconst dotIdx = basename.lastIndexOf(\".\");\n\t\t\tconst ext = dotIdx > 0 ? basename.slice(dotIdx).toLowerCase() : \"\";\n\t\t\tconst storageKey = `${keyPrefix}${ext}`;\n\n\t\t\t// Upload to storage first\n\t\t\tawait storage.upload({\n\t\t\t\tkey: storageKey,\n\t\t\t\tbody: new Uint8Array(bytes),\n\t\t\t\tcontentType,\n\t\t\t});\n\n\t\t\t// Create DB record — clean up storage on failure\n\t\t\tlet media;\n\t\t\ttry {\n\t\t\t\tmedia = await mediaRepo.create({\n\t\t\t\t\tfilename: basename,\n\t\t\t\t\tmimeType: contentType,\n\t\t\t\t\tsize: bytes.byteLength,\n\t\t\t\t\tstorageKey,\n\t\t\t\t\tstatus: \"ready\",\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\ttry {\n\t\t\t\t\tawait storage.delete(storageKey);\n\t\t\t\t} catch {\n\t\t\t\t\t// Best-effort cleanup\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tmediaId: media.id,\n\t\t\t\tstorageKey,\n\t\t\t\turl: `/_emdash/api/media/file/${storageKey}`,\n\t\t\t};\n\t\t},\n\n\t\tasync delete(id: string): Promise<boolean> {\n\t\t\tconst deleted = await mediaRepo.delete(id);\n\t\t\t// Plugins can delete media that's referenced by site settings\n\t\t\t// (`logo`, `favicon`, `seo.defaultOgImage`); the worker-scoped\n\t\t\t// resolved-URL cache must be dropped or it will keep serving\n\t\t\t// 404s. Matches the invalidation in\n\t\t\t// `EmDashRuntime.handleMediaDelete`.\n\t\t\tif (deleted) {\n\t\t\t\tinvalidateSiteSettingsCache();\n\t\t\t}\n\t\t\treturn deleted;\n\t\t},\n\t};\n}\n\n// =============================================================================\n// HTTP Access\n// =============================================================================\n\n/** Maximum number of redirects to follow in plugin HTTP access */\nconst MAX_PLUGIN_REDIRECTS = 5;\n\n/**\n * Check if a hostname matches any pattern in the allowed list.\n * Patterns: \"*\" matches all, \"*.example.com\" matches subdomains AND bare \"example.com\",\n * \"api.example.com\" matches exactly.\n */\nfunction isHostAllowed(host: string, allowedHosts: string[]): boolean {\n\treturn allowedHosts.some((pattern) => {\n\t\tif (pattern === \"*\") return true;\n\t\tif (pattern.startsWith(\"*.\")) {\n\t\t\tconst suffix = pattern.slice(1); // \".example.com\"\n\t\t\t// Match subdomains (foo.example.com) and bare domain (example.com)\n\t\t\treturn host.endsWith(suffix) || host === pattern.slice(2);\n\t\t}\n\t\treturn host === pattern;\n\t});\n}\n\n/**\n * Create HTTP access with host validation.\n *\n * Uses redirect: \"manual\" to re-validate each redirect target against\n * the allowedHosts list, preventing redirects to unauthorized hosts.\n */\nexport function createHttpAccess(pluginId: string, allowedHosts: string[]): HttpAccess {\n\treturn {\n\t\tasync fetch(url: string, init?: RequestInit): Promise<Response> {\n\t\t\t// Deny by default — plugins must declare allowed hosts\n\t\t\tif (allowedHosts.length === 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Plugin \"${pluginId}\" has no allowed hosts configured. ` +\n\t\t\t\t\t\t`Add hosts to the plugin's allowedHosts array to enable HTTP requests.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet currentUrl = url;\n\t\t\tlet currentInit = init;\n\n\t\t\tfor (let i = 0; i <= MAX_PLUGIN_REDIRECTS; i++) {\n\t\t\t\tconst hostname = new URL(currentUrl).hostname;\n\t\t\t\tif (!isHostAllowed(hostname, allowedHosts)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Plugin \"${pluginId}\" is not allowed to fetch from host \"${hostname}\". ` +\n\t\t\t\t\t\t\t`Allowed hosts: ${allowedHosts.join(\", \")}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst response = await globalThis.fetch(currentUrl, {\n\t\t\t\t\t...currentInit,\n\t\t\t\t\tredirect: \"manual\",\n\t\t\t\t});\n\n\t\t\t\t// Not a redirect -- return directly\n\t\t\t\tif (response.status < 300 || response.status >= 400) {\n\t\t\t\t\treturn response;\n\t\t\t\t}\n\n\t\t\t\t// Extract redirect target\n\t\t\t\tconst location = response.headers.get(\"Location\");\n\t\t\t\tif (!location) {\n\t\t\t\t\treturn response;\n\t\t\t\t}\n\n\t\t\t\t// Resolve relative redirects; strip credentials on cross-origin hops\n\t\t\t\tconst previousOrigin = new URL(currentUrl).origin;\n\t\t\t\tcurrentUrl = new URL(location, currentUrl).href;\n\t\t\t\tconst nextOrigin = new URL(currentUrl).origin;\n\n\t\t\t\tif (previousOrigin !== nextOrigin && currentInit) {\n\t\t\t\t\tcurrentInit = stripCredentialHeaders(currentInit);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrow new Error(`Plugin \"${pluginId}\": too many redirects (max ${MAX_PLUGIN_REDIRECTS})`);\n\t\t},\n\t};\n}\n\n/**\n * Create unrestricted HTTP access (for plugins with network:fetch:any capability).\n * No host validation, but applies SSRF protection on redirect targets to\n * prevent plugins from being tricked into reaching internal services.\n */\nexport function createUnrestrictedHttpAccess(pluginId: string): HttpAccess {\n\treturn {\n\t\tasync fetch(url: string, init?: RequestInit): Promise<Response> {\n\t\t\tlet currentUrl = url;\n\t\t\tlet currentInit = init;\n\n\t\t\tfor (let i = 0; i <= MAX_PLUGIN_REDIRECTS; i++) {\n\t\t\t\t// Validate each URL against SSRF rules (private IPs, metadata\n\t\t\t\t// endpoints, wildcard DNS, resolved-IP private ranges).\n\t\t\t\ttry {\n\t\t\t\t\tawait resolveAndValidateExternalUrl(currentUrl);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconst msg = e instanceof SsrfError ? e.message : \"SSRF validation failed\";\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Plugin \"${pluginId}\": blocked fetch to \"${new URL(currentUrl).hostname}\": ${msg}`,\n\t\t\t\t\t\t{ cause: e },\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst response = await globalThis.fetch(currentUrl, {\n\t\t\t\t\t...currentInit,\n\t\t\t\t\tredirect: \"manual\",\n\t\t\t\t});\n\n\t\t\t\t// Not a redirect -- return directly\n\t\t\t\tif (response.status < 300 || response.status >= 400) {\n\t\t\t\t\treturn response;\n\t\t\t\t}\n\n\t\t\t\t// Extract redirect target\n\t\t\t\tconst location = response.headers.get(\"Location\");\n\t\t\t\tif (!location) {\n\t\t\t\t\treturn response;\n\t\t\t\t}\n\n\t\t\t\t// Resolve relative redirects; strip credentials on cross-origin hops\n\t\t\t\tconst previousOrigin = new URL(currentUrl).origin;\n\t\t\t\tcurrentUrl = new URL(location, currentUrl).href;\n\t\t\t\tconst nextOrigin = new URL(currentUrl).origin;\n\n\t\t\t\tif (previousOrigin !== nextOrigin && currentInit) {\n\t\t\t\t\tcurrentInit = stripCredentialHeaders(currentInit);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrow new Error(`Plugin \"${pluginId}\": too many redirects (max ${MAX_PLUGIN_REDIRECTS})`);\n\t\t},\n\t};\n}\n\n/**\n * Create blocked HTTP access (for plugins without network:request capability)\n */\nexport function createBlockedHttpAccess(pluginId: string): HttpAccess {\n\treturn {\n\t\tasync fetch(): Promise<never> {\n\t\t\tthrow new Error(\n\t\t\t\t`Plugin \"${pluginId}\" does not have the \"network:request\" capability. ` +\n\t\t\t\t\t`Add \"network:request\" to the plugin's capabilities to enable HTTP requests.`,\n\t\t\t);\n\t\t},\n\t};\n}\n\n// =============================================================================\n// Log Access\n// =============================================================================\n\n/**\n * Create logger for a plugin\n */\nexport function createLogAccess(pluginId: string): LogAccess {\n\tconst prefix = `[plugin:${pluginId}]`;\n\n\treturn {\n\t\tdebug(message: string, data?: unknown): void {\n\t\t\tif (data !== undefined) {\n\t\t\t\tconsole.debug(prefix, message, data);\n\t\t\t} else {\n\t\t\t\tconsole.debug(prefix, message);\n\t\t\t}\n\t\t},\n\n\t\tinfo(message: string, data?: unknown): void {\n\t\t\tif (data !== undefined) {\n\t\t\t\tconsole.info(prefix, message, data);\n\t\t\t} else {\n\t\t\t\tconsole.info(prefix, message);\n\t\t\t}\n\t\t},\n\n\t\twarn(message: string, data?: unknown): void {\n\t\t\tif (data !== undefined) {\n\t\t\t\tconsole.warn(prefix, message, data);\n\t\t\t} else {\n\t\t\t\tconsole.warn(prefix, message);\n\t\t\t}\n\t\t},\n\n\t\terror(message: string, data?: unknown): void {\n\t\t\tif (data !== undefined) {\n\t\t\t\tconsole.error(prefix, message, data);\n\t\t\t} else {\n\t\t\t\tconsole.error(prefix, message);\n\t\t\t}\n\t\t},\n\t};\n}\n\n// =============================================================================\n// Site Info\n// =============================================================================\n\nconst TRAILING_SLASH_RE = /\\/$/;\n\n/**\n * Options for creating site info\n */\nexport interface SiteInfoOptions {\n\t/** Site name from options table */\n\tsiteName?: string;\n\t/** Site URL from options table or Astro config */\n\tsiteUrl?: string;\n\t/** Site locale from options table */\n\tlocale?: string;\n}\n\n/**\n * Create site info from config and settings.\n *\n * Resolution order for URL:\n * 1. options table (emdash:site_url)\n * 2. Astro `site` config\n * 3. fallback to empty string\n */\nexport function createSiteInfo(options: SiteInfoOptions): SiteInfo {\n\treturn {\n\t\tname: options.siteName ?? \"\",\n\t\turl: (options.siteUrl ?? \"\").replace(TRAILING_SLASH_RE, \"\"), // strip trailing slash\n\t\tlocale: options.locale ?? \"en\",\n\t};\n}\n\n/**\n * Create a URL helper that generates absolute URLs from relative paths.\n * Validates that path starts with \"/\" and rejects protocol-relative paths (\"//\").\n */\nexport function createUrlHelper(siteUrl: string): (path: string) => string {\n\tconst base = siteUrl.replace(TRAILING_SLASH_RE, \"\"); // strip trailing slash\n\n\treturn (path: string): string => {\n\t\tif (!path.startsWith(\"/\")) {\n\t\t\tthrow new Error(`URL path must start with \"/\", got: \"${path}\"`);\n\t\t}\n\t\tif (path.startsWith(\"//\")) {\n\t\t\tthrow new Error(`URL path must not be protocol-relative, got: \"${path}\"`);\n\t\t}\n\t\treturn `${base}${path}`;\n\t};\n}\n\n// =============================================================================\n// User Access\n// =============================================================================\n\n/**\n * Convert a UserRepository user to the plugin-facing UserInfo shape.\n * Strips sensitive fields (avatarUrl, emailVerified, data).\n */\nfunction toUserInfo(user: {\n\tid: string;\n\temail: string;\n\tname: string | null;\n\trole: number;\n\tcreatedAt: string;\n}): UserInfo {\n\treturn {\n\t\tid: user.id,\n\t\temail: user.email,\n\t\tname: user.name,\n\t\trole: user.role,\n\t\tcreatedAt: user.createdAt,\n\t};\n}\n\n/**\n * Create read-only user access for plugins.\n * Excludes sensitive fields (password hashes, sessions, passkeys, avatar URL, data).\n */\nexport function createUserAccess(db: Kysely<Database>): UserAccess {\n\tconst userRepo = new UserRepository(db);\n\n\treturn {\n\t\tasync get(id: string): Promise<UserInfo | null> {\n\t\t\tconst user = await userRepo.findById(id);\n\t\t\tif (!user) return null;\n\t\t\treturn toUserInfo(user);\n\t\t},\n\n\t\tasync getByEmail(email: string): Promise<UserInfo | null> {\n\t\t\tconst user = await userRepo.findByEmail(email);\n\t\t\tif (!user) return null;\n\t\t\treturn toUserInfo(user);\n\t\t},\n\n\t\tasync list(opts?: {\n\t\t\trole?: number;\n\t\t\tlimit?: number;\n\t\t\tcursor?: string;\n\t\t}): Promise<{ items: UserInfo[]; nextCursor?: string }> {\n\t\t\tconst result = await userRepo.findMany({\n\t\t\t\trole: opts?.role as 10 | 20 | 30 | 40 | 50 | undefined,\n\t\t\t\tcursor: opts?.cursor,\n\t\t\t\tlimit: opts?.limit,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\titems: result.items.map(toUserInfo),\n\t\t\t\tnextCursor: result.nextCursor,\n\t\t\t};\n\t\t},\n\t};\n}\n\n// =============================================================================\n// Plugin Context Factory\n// =============================================================================\n\nexport interface PluginContextFactoryOptions {\n\tdb: Kysely<Database>;\n\t/**\n\t * Storage backend for direct media uploads.\n\t * If not provided, upload() will throw.\n\t */\n\tstorage?: Storage;\n\t/**\n\t * Function to generate upload URLs for media.\n\t * If not provided, media write operations will throw.\n\t */\n\tgetUploadUrl?: (\n\t\tfilename: string,\n\t\tcontentType: string,\n\t) => Promise<{ uploadUrl: string; mediaId: string }>;\n\t/**\n\t * Site information for ctx.site and ctx.url().\n\t * If not provided, site info will have empty defaults.\n\t */\n\tsiteInfo?: SiteInfoOptions;\n\t/**\n\t * Callback to notify the cron scheduler that the next due time may have changed.\n\t * If not provided, ctx.cron will not be available.\n\t */\n\tcronReschedule?: () => void;\n\t/**\n\t * Email pipeline instance for ctx.email.\n\t * If not provided (or no provider configured), ctx.email will be undefined.\n\t */\n\temailPipeline?: EmailPipeline;\n\t/**\n\t * Pre-resolved list of trusted proxy header names (from the runtime\n\t * `EmDashConfig.trustedProxyHeaders` or the env var). Plugin route\n\t * handlers pass this to `extractRequestMeta` so plugins see the same\n\t * client IP the core auth path does.\n\t */\n\ttrustedProxyHeaders?: string[];\n}\n\n/**\n * Factory for creating plugin contexts\n */\nexport class PluginContextFactory {\n\tprivate optionsRepo: OptionsRepository;\n\tprivate db: Kysely<Database>;\n\tprivate storage?: Storage;\n\tprivate getUploadUrl?: (\n\t\tfilename: string,\n\t\tcontentType: string,\n\t) => Promise<{ uploadUrl: string; mediaId: string }>;\n\tprivate site: SiteInfo;\n\tprivate urlHelper: (path: string) => string;\n\tprivate cronReschedule?: () => void;\n\tprivate emailPipeline?: EmailPipeline;\n\n\tconstructor(options: PluginContextFactoryOptions) {\n\t\tthis.db = options.db;\n\t\tthis.optionsRepo = new OptionsRepository(options.db);\n\t\tthis.storage = options.storage;\n\t\tthis.getUploadUrl = options.getUploadUrl;\n\t\tthis.site = createSiteInfo(options.siteInfo ?? {});\n\t\tthis.urlHelper = createUrlHelper(this.site.url);\n\t\tthis.cronReschedule = options.cronReschedule;\n\t\tthis.emailPipeline = options.emailPipeline;\n\t}\n\n\t/**\n\t * Create the unified plugin context\n\t */\n\tcreateContext(plugin: ResolvedPlugin): PluginContext {\n\t\tconst capabilities = new Set(plugin.capabilities);\n\n\t\t// Always available\n\t\tconst kv = createKVAccess(this.optionsRepo, plugin.id);\n\t\tconst log = createLogAccess(plugin.id);\n\t\tconst storage = createStorageAccess(this.db, plugin.id, plugin.storage);\n\n\t\t// Capability-gated: content\n\t\t// Note: capabilities reach this point already normalized to the\n\t\t// canonical names by definePlugin / adaptSandboxEntry. Deprecated\n\t\t// names (\"read:content\", \"write:content\") never appear here.\n\t\tlet content: ContentAccess | ContentAccessWithWrite | undefined;\n\t\tif (capabilities.has(\"content:write\")) {\n\t\t\tcontent = createContentAccessWithWrite(this.db);\n\t\t} else if (capabilities.has(\"content:read\")) {\n\t\t\tcontent = createContentAccess(this.db);\n\t\t}\n\n\t\t// Capability-gated: media\n\t\tlet media: MediaAccess | MediaAccessWithWrite | undefined;\n\t\tif (capabilities.has(\"media:write\") && this.getUploadUrl) {\n\t\t\tmedia = createMediaAccessWithWrite(this.db, this.getUploadUrl, this.storage);\n\t\t} else if (capabilities.has(\"media:read\")) {\n\t\t\tmedia = createMediaAccess(this.db);\n\t\t}\n\n\t\t// Capability-gated: http\n\t\tlet http: HttpAccess | undefined;\n\t\tif (capabilities.has(\"network:request:unrestricted\")) {\n\t\t\thttp = createUnrestrictedHttpAccess(plugin.id);\n\t\t} else if (capabilities.has(\"network:request\")) {\n\t\t\thttp = createHttpAccess(plugin.id, plugin.allowedHosts);\n\t\t}\n\n\t\t// Capability-gated: users\n\t\tlet users: UserAccess | undefined;\n\t\tif (capabilities.has(\"users:read\")) {\n\t\t\tusers = createUserAccess(this.db);\n\t\t}\n\n\t\t// Cron access ��� always available (scoped to plugin), but only if\n\t\t// the runtime provided a reschedule callback (i.e. cron is wired up).\n\t\tlet cron: CronAccess | undefined;\n\t\tif (this.cronReschedule) {\n\t\t\tcron = new CronAccessImpl(this.db, plugin.id, this.cronReschedule);\n\t\t}\n\n\t\t// Email access — requires email:send capability AND a configured provider\n\t\tlet email: EmailAccess | undefined;\n\t\tif (capabilities.has(\"email:send\") && this.emailPipeline?.isAvailable()) {\n\t\t\tconst pipeline = this.emailPipeline;\n\t\t\tconst pluginId = plugin.id;\n\t\t\temail = {\n\t\t\t\tsend: (message) => pipeline.send(message, pluginId),\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tplugin: {\n\t\t\t\tid: plugin.id,\n\t\t\t\tversion: plugin.version,\n\t\t\t},\n\t\t\tstorage,\n\t\t\tkv,\n\t\t\tcontent,\n\t\t\tmedia,\n\t\t\thttp,\n\t\t\tlog,\n\t\t\tsite: this.site,\n\t\t\turl: this.urlHelper,\n\t\t\tusers,\n\t\t\tcron,\n\t\t\temail,\n\t\t};\n\t}\n}\n\n/**\n * Create a plugin context for a resolved plugin\n */\nexport function createPluginContext(\n\toptions: PluginContextFactoryOptions,\n\tplugin: ResolvedPlugin,\n): PluginContext {\n\tconst factory = new PluginContextFactory(options);\n\treturn factory.createContext(plugin);\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cron-DdEVrQ2Y.mjs","names":[],"sources":["../src/plugins/cron.ts"],"sourcesContent":["/**\n * Plugin Cron System\n *\n * Provides scheduled task execution for plugins:\n * - CronExecutor: claims overdue tasks, invokes per-plugin cron hook, updates next run.\n * - CronAccessImpl: per-plugin API for schedule/cancel/list.\n *\n */\n\nimport { Cron } from \"croner\";\nimport type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport type { Database } from \"../database/types.js\";\nimport type { CronAccess, CronEvent, CronTaskInfo } from \"./types.js\";\n\n/** Stale lock threshold in minutes */\nconst STALE_LOCK_MINUTES = 10;\n\n/**\n * Callback to invoke a plugin's cron hook.\n * Provided by PluginManager so CronExecutor stays decoupled from the hook pipeline.\n */\nexport type InvokeCronHookFn = (pluginId: string, event: CronEvent) => Promise<void>;\n\n/**\n * Callback to notify the scheduler that the next due time may have changed.\n */\nexport type RescheduleFn = () => void;\n\n// ─── CronExecutor ──────────────────────────────────────────────────────────\n\n/**\n * Executes overdue cron tasks.\n *\n * Called by the platform driver: the NodeCronScheduler timer on Node, or the\n * Worker's `scheduled()` handler (via runScheduledTasks) on Cloudflare.\n * Stateless — all state lives in the database.\n */\nexport class CronExecutor {\n\tconstructor(\n\t\tprivate db: Kysely<Database>,\n\t\tprivate invokeCronHook: InvokeCronHookFn,\n\t) {}\n\n\t/**\n\t * Process all overdue tasks.\n\t *\n\t * 1. Atomically claim tasks whose next_run_at <= now, status = idle, enabled = 1.\n\t * 2. For each claimed task, invoke the plugin's cron hook.\n\t * 3. On success: compute next_run_at and reset to idle, or delete one-shots.\n\t * 4. On failure: reset to idle (retry on next tick).\n\t */\n\tasync tick(): Promise<number> {\n\t\tconst now = new Date().toISOString();\n\t\tlet processed = 0;\n\n\t\t// Claim overdue tasks atomically\n\t\tconst claimed = await sql<{\n\t\t\tid: string;\n\t\t\tplugin_id: string;\n\t\t\ttask_name: string;\n\t\t\tschedule: string;\n\t\t\tis_oneshot: number;\n\t\t\tdata: string | null;\n\t\t\tnext_run_at: string;\n\t\t}>`\n\t\t\tUPDATE _emdash_cron_tasks\n\t\t\tSET status = 'running', locked_at = ${now}\n\t\t\tWHERE id IN (\n\t\t\t\tSELECT id FROM _emdash_cron_tasks\n\t\t\t\tWHERE next_run_at <= ${now}\n\t\t\t\t AND status = 'idle'\n\t\t\t\t AND enabled = 1\n\t\t\t\tORDER BY next_run_at ASC\n\t\t\t\tLIMIT 10\n\t\t\t)\n\t\t\tRETURNING id, plugin_id, task_name, schedule, is_oneshot, data, next_run_at\n\t\t`.execute(this.db);\n\n\t\tfor (const task of claimed.rows) {\n\t\t\t// Parse task data safely ��� malformed JSON must not crash the entire batch\n\t\t\tlet parsedData: Record<string, unknown> | undefined;\n\t\t\tif (task.data) {\n\t\t\t\ttry {\n\t\t\t\t\tparsedData = JSON.parse(task.data) as Record<string, unknown>;\n\t\t\t\t} catch {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`[cron] Invalid JSON data for ${task.plugin_id}:${task.task_name}, skipping`,\n\t\t\t\t\t);\n\t\t\t\t\tawait sql`\n\t\t\t\t\t\tUPDATE _emdash_cron_tasks\n\t\t\t\t\t\tSET status = 'idle', locked_at = NULL\n\t\t\t\t\t\tWHERE id = ${task.id}\n\t\t\t\t\t`.execute(this.db);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst event: CronEvent = {\n\t\t\t\tname: task.task_name,\n\t\t\t\tdata: parsedData,\n\t\t\t\tscheduledAt: task.next_run_at,\n\t\t\t};\n\n\t\t\tlet hookFailed = false;\n\t\t\ttry {\n\t\t\t\tawait this.invokeCronHook(task.plugin_id, event);\n\t\t\t} catch (error) {\n\t\t\t\thookFailed = true;\n\t\t\t\tconsole.error(`[cron] Hook failed for ${task.plugin_id}:${task.task_name}:`, error);\n\t\t\t}\n\n\t\t\tif (task.is_oneshot) {\n\t\t\t\tif (hookFailed) {\n\t\t\t\t\t// Retry metadata is namespaced under __emdash to avoid collisions\n\t\t\t\t\t// with plugin-controlled data fields.\n\t\t\t\t\tconst meta =\n\t\t\t\t\t\tparsedData?.__emdash != null && typeof parsedData.__emdash === \"object\"\n\t\t\t\t\t\t\t? (parsedData.__emdash as Record<string, unknown>)\n\t\t\t\t\t\t\t: undefined;\n\t\t\t\t\tconst raw = meta?.retryCount;\n\t\t\t\t\tconst retryCount =\n\t\t\t\t\t\ttypeof raw === \"number\" && Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 0;\n\t\t\t\t\tconst MAX_ONESHOT_RETRIES = 5;\n\n\t\t\t\t\tif (retryCount >= MAX_ONESHOT_RETRIES) {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t`[cron] One-shot task ${task.plugin_id}:${task.task_name} exceeded ${MAX_ONESHOT_RETRIES} retries, removing`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tawait sql`\n\t\t\t\t\t\tDELETE FROM _emdash_cron_tasks WHERE id = ${task.id}\n\t\t\t\t\t`.execute(this.db);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Retry with exponential backoff: 1m, 2m, 4m, 8m, 16m\n\t\t\t\t\t\tconst backoffMs = 60_000 * Math.pow(2, retryCount);\n\t\t\t\t\t\tconst retryAt = new Date(Date.now() + backoffMs).toISOString();\n\t\t\t\t\t\tconst updatedData = JSON.stringify({\n\t\t\t\t\t\t\t...parsedData,\n\t\t\t\t\t\t\t__emdash: { ...meta, retryCount: retryCount + 1 },\n\t\t\t\t\t\t});\n\t\t\t\t\t\tawait sql`\n\t\t\t\t\t\tUPDATE _emdash_cron_tasks\n\t\t\t\t\t\tSET status = 'idle', locked_at = NULL, next_run_at = ${retryAt}, data = ${updatedData}\n\t\t\t\t\t\tWHERE id = ${task.id}\n\t\t\t\t\t`.execute(this.db);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Success: delete the one-shot task\n\t\t\t\t\tawait sql`\n\t\t\t\t\t\tDELETE FROM _emdash_cron_tasks WHERE id = ${task.id}\n\t\t\t\t\t`.execute(this.db);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Recurring: compute next run and reset\n\t\t\t\tconst nextRun = nextCronTime(task.schedule);\n\t\t\t\tawait sql`\n\t\t\t\t\tUPDATE _emdash_cron_tasks\n\t\t\t\t\tSET status = 'idle',\n\t\t\t\t\t\tlocked_at = NULL,\n\t\t\t\t\t\tlast_run_at = ${now},\n\t\t\t\t\t\tnext_run_at = ${nextRun}\n\t\t\t\t\tWHERE id = ${task.id}\n\t\t\t\t`.execute(this.db);\n\t\t\t}\n\n\t\t\tprocessed++;\n\t\t}\n\n\t\treturn processed;\n\t}\n\n\t/**\n\t * Recover tasks stuck in 'running' for more than STALE_LOCK_MINUTES.\n\t * These likely crashed mid-execution.\n\t */\n\tasync recoverStaleLocks(): Promise<number> {\n\t\tconst cutoff = new Date(Date.now() - STALE_LOCK_MINUTES * 60 * 1000).toISOString();\n\n\t\tconst result = await sql`\n\t\t\tUPDATE _emdash_cron_tasks\n\t\t\tSET status = 'idle', locked_at = NULL\n\t\t\tWHERE status = 'running'\n\t\t\t AND locked_at < ${cutoff}\n\t\t`.execute(this.db);\n\n\t\treturn Number(result.numAffectedRows ?? 0);\n\t}\n\n\t/**\n\t * Get the next due time across all enabled tasks.\n\t * Returns null if no tasks are scheduled.\n\t */\n\tasync getNextDueTime(): Promise<string | null> {\n\t\tconst result = await sql<{ next: string | null }>`\n\t\t\tSELECT MIN(next_run_at) as next\n\t\t\tFROM _emdash_cron_tasks\n\t\t\tWHERE status = 'idle' AND enabled = 1\n\t\t`.execute(this.db);\n\n\t\treturn result.rows[0]?.next ?? null;\n\t}\n}\n\n// ─── CronAccessImpl ────────────────────────────────────────────────────────\n\n/**\n * Per-plugin cron API implementation.\n * Scoped to a single plugin ID — plugins cannot see or modify other plugins' tasks.\n */\nexport class CronAccessImpl implements CronAccess {\n\tconstructor(\n\t\tprivate db: Kysely<Database>,\n\t\tprivate pluginId: string,\n\t\tprivate reschedule: RescheduleFn,\n\t) {}\n\n\tasync schedule(\n\t\tname: string,\n\t\topts: { schedule: string; data?: Record<string, unknown> },\n\t): Promise<void> {\n\t\tvalidateTaskName(name);\n\t\tvalidateSchedule(opts.schedule);\n\n\t\tconst oneshot = isOneShot(opts.schedule);\n\t\tconst nextRun = oneshot ? opts.schedule : nextCronTime(opts.schedule);\n\t\tconst dataJson = opts.data ? JSON.stringify(opts.data) : null;\n\t\tconst id = ulid();\n\n\t\t// Upsert: if task already exists for this plugin+name, update it.\n\t\t// Guard: don't clobber a task that is currently executing.\n\t\tawait sql`\n\t\t\tINSERT INTO _emdash_cron_tasks (id, plugin_id, task_name, schedule, is_oneshot, data, next_run_at, status, enabled)\n\t\t\tVALUES (${id}, ${this.pluginId}, ${name}, ${opts.schedule}, ${oneshot ? 1 : 0}, ${dataJson}, ${nextRun}, 'idle', 1)\n\t\t\tON CONFLICT (plugin_id, task_name) DO UPDATE SET\n\t\t\t\tschedule = ${opts.schedule},\n\t\t\t\tis_oneshot = ${oneshot ? 1 : 0},\n\t\t\t\tdata = ${dataJson},\n\t\t\t\tnext_run_at = ${nextRun},\n\t\t\t\tstatus = CASE WHEN _emdash_cron_tasks.status = 'running' THEN 'running' ELSE 'idle' END,\n\t\t\t\tlocked_at = CASE WHEN _emdash_cron_tasks.status = 'running' THEN _emdash_cron_tasks.locked_at ELSE NULL END,\n\t\t\t\tenabled = 1\n\t\t`.execute(this.db);\n\n\t\tthis.reschedule();\n\t}\n\n\tasync cancel(name: string): Promise<void> {\n\t\tawait sql`\n\t\t\tDELETE FROM _emdash_cron_tasks\n\t\t\tWHERE plugin_id = ${this.pluginId} AND task_name = ${name}\n\t\t`.execute(this.db);\n\n\t\tthis.reschedule();\n\t}\n\n\tasync list(): Promise<CronTaskInfo[]> {\n\t\tconst rows = await sql<{\n\t\t\ttask_name: string;\n\t\t\tschedule: string;\n\t\t\tnext_run_at: string;\n\t\t\tlast_run_at: string | null;\n\t\t}>`\n\t\t\tSELECT task_name, schedule, next_run_at, last_run_at\n\t\t\tFROM _emdash_cron_tasks\n\t\t\tWHERE plugin_id = ${this.pluginId} AND enabled = 1\n\t\t\tORDER BY next_run_at ASC\n\t\t`.execute(this.db);\n\n\t\treturn rows.rows.map((row) => ({\n\t\t\tname: row.task_name,\n\t\t\tschedule: row.schedule,\n\t\t\tnextRunAt: row.next_run_at,\n\t\t\tlastRunAt: row.last_run_at,\n\t\t}));\n\t}\n}\n\n// ─── Cron task lifecycle helpers ────────────────────────────────────────────\n\n/**\n * Enable or disable all cron tasks for a plugin.\n * Called by admin disable/enable endpoints and PluginManager lifecycle.\n * Gracefully handles the cron table not existing yet (pre-migration).\n */\nexport async function setCronTasksEnabled(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n\tenabled: boolean,\n): Promise<void> {\n\ttry {\n\t\tawait sql`\n\t\t\tUPDATE _emdash_cron_tasks\n\t\t\tSET enabled = ${enabled ? 1 : 0}\n\t\t\tWHERE plugin_id = ${pluginId}\n\t\t`.execute(db);\n\t} catch {\n\t\t// Cron table may not exist yet (pre-migration). Non-fatal.\n\t}\n}\n\n// ─── Cron utilities ────────────────────────────────────────────────────────\n\n/**\n * Compute the next fire time for a cron expression.\n * Supports standard cron (5-field), extended (6-field with seconds), and\n * aliases like @daily, @weekly, @hourly, @monthly, @yearly.\n */\nexport function nextCronTime(expression: string): string {\n\tconst job = new Cron(expression);\n\tconst next = job.nextRun();\n\tif (!next) {\n\t\tthrow new Error(`Invalid cron expression or no future run: \"${expression}\"`);\n\t}\n\treturn next.toISOString();\n}\n\n/**\n * Check whether a string is a valid cron expression.\n */\nfunction isCronExpression(schedule: string): boolean {\n\ttry {\n\t\t// Cron constructor validates; we discard the instance immediately.\n\t\tconst _cron = new Cron(schedule);\n\t\tvoid _cron;\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Check if a schedule string is a one-shot (ISO 8601 datetime) rather than\n * a recurring cron expression.\n *\n * Tries to parse as a cron expression first. Only if that fails does it\n * attempt Date.parse. This avoids misclassifying cron range expressions\n * like \"1-5 * * * *\" which Date.parse accepts as valid dates.\n */\nexport function isOneShot(schedule: string): boolean {\n\tif (schedule.startsWith(\"@\")) return false;\n\tif (isCronExpression(schedule)) return false;\n\treturn !isNaN(Date.parse(schedule));\n}\n\n/** Max length for a task name */\nconst MAX_TASK_NAME_LENGTH = 128;\n/** Task name pattern: alphanumeric, dashes, underscores */\nconst TASK_NAME_RE = /^[a-zA-Z][a-zA-Z0-9_-]*$/;\n\n/**\n * Validate a cron task name.\n * Must be non-empty, ≤128 chars, alphanumeric with dashes/underscores.\n */\nexport function validateTaskName(name: string): void {\n\tif (!name || name.length > MAX_TASK_NAME_LENGTH) {\n\t\tthrow new Error(\n\t\t\t`Invalid task name: must be 1-${MAX_TASK_NAME_LENGTH} characters, got ${name.length}`,\n\t\t);\n\t}\n\tif (!TASK_NAME_RE.test(name)) {\n\t\tthrow new Error(\n\t\t\t`Invalid task name \"${name}\": must start with a letter and contain only letters, numbers, dashes, or underscores`,\n\t\t);\n\t}\n}\n\n/**\n * Validate a schedule string at registration time.\n * Must be a valid cron expression or a parseable ISO 8601 datetime.\n */\nexport function validateSchedule(schedule: string): void {\n\tif (!schedule || schedule.length > 256) {\n\t\tthrow new Error(`Invalid schedule: must be 1-256 characters, got ${schedule.length}`);\n\t}\n\n\t// Try cron first\n\tif (isCronExpression(schedule)) return;\n\n\tconst parsed = Date.parse(schedule);\n\tif (isNaN(parsed)) {\n\t\tthrow new Error(\n\t\t\t`Invalid schedule \"${schedule}\": must be a valid cron expression or ISO 8601 datetime`,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-D2VAiumu.d.mts","names":[],"sources":["../src/database/errors.ts","../src/database/connection.ts","../src/database/repositories/content.ts","../src/database/repositories/media.ts","../src/database/repositories/user.ts","../src/database/repositories/revision.ts","../src/database/repositories/comment.ts","../src/database/repositories/plugin-storage.ts","../src/fields/types.ts","../src/fields/image.ts","../src/fields/file.ts","../src/fields/reference.ts","../src/fields/portable-text.ts","../src/api/handlers/content.ts","../src/api/handlers/manifest.ts","../src/api/handlers/revision.ts","../src/api/handlers/media.ts","../src/schema/registry.ts","../src/schema/query.ts","../src/sections/types.ts","../src/sections/index.ts","../src/plugins/sandbox/types.ts","../src/registry/types.ts","../src/content/converters/types.ts","../src/content/converters/prosemirror-to-portable-text.ts","../src/content/converters/portable-text-to-prosemirror.ts","../src/utils/hash.ts","../src/utils/url.ts","../src/loader.ts","../src/visual-editing/editable.ts","../src/query.ts","../src/after.ts","../src/i18n/config.ts","../src/plugins/define-plugin.ts","../src/astro/storage/types.ts","../src/astro/integration/runtime.ts","../src/plugins/manifest-schema.ts","../src/plugins/hooks.ts","../src/plugins/email.ts","../src/plugins/context.ts","../src/plugins/routes.ts","../src/plugins/manager.ts","../src/plugins/cron.ts","../src/plugins/scheduler/types.ts","../src/plugins/scheduler/node.ts","../src/plugins/sandbox/noop.ts","../src/preview/tokens.ts","../src/preview/urls.ts","../src/preview/helpers.ts","../src/settings/index.ts","../src/comments/query.ts","../src/menus/types.ts","../src/menus/index.ts","../src/bylines/index.ts","../src/taxonomies/types.ts","../src/taxonomies/index.ts","../src/widgets/types.ts","../src/widgets/index.ts","../src/search/types.ts","../src/search/fts-manager.ts","../src/search/query.ts","../src/search/text-extraction.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;cAKa,mBAAA,SAA4B,KAAA;EAGvB,KAAA;cADhB,OAAA,UACgB,KAAA;AAAA;;;UCCD,cAAA;EAChB,GAAA;EACA,SAAA;AAAA;;;;;;;;;cC8GY,iBAAA;EAAA,QACQ,EAAA;cAAA,EAAA,EAAI,MAAA,CAAO,QAAA;;;;EAKzB,MAAA,CAAO,KAAA,EAAO,kBAAA,GAAqB,OAAA,CAAQ,WAAA;;;AF1HlD;;;;;;;EE6NO,kBAAA,CAAmB,IAAA,UAAc,IAAA,UAAc,MAAA,YAAkB,OAAA;EF1NtD;;;;;EEkRX,SAAA,CAAU,IAAA,UAAc,EAAA,UAAY,QAAA,YAAoB,OAAA,CAAQ,WAAA;EDjRtD;;;EC2TV,QAAA,CAAS,IAAA,UAAc,EAAA,WAAa,OAAA,CAAQ,WAAA;EDzTzC;;;;EC8UH,wBAAA,CAAyB,IAAA,UAAc,EAAA,WAAa,OAAA,CAAQ,WAAA;EAhOrC;;;;EAoPvB,cAAA,CACL,IAAA,UACA,UAAA,UACA,MAAA,YACE,OAAA,CAAQ,WAAA;EAlPsC;;;;EA0P3C,8BAAA,CACL,IAAA,UACA,UAAA,UACA,MAAA,YACE,OAAA,CAAQ,WAAA;EAAA,QAIG,eAAA;EA7D4B;;;EA4FpC,UAAA,CAAW,IAAA,UAAc,IAAA,UAAc,MAAA,YAAkB,OAAA,CAAQ,WAAA;EA/CpE;;;;EA6EG,0BAAA,CACL,IAAA,UACA,IAAA,UACA,MAAA,YACE,OAAA,CAAQ,WAAA;EAAA;;;EA2BL,QAAA,CACL,IAAA,UACA,OAAA,GAAS,eAAA,GACP,OAAA,CAAQ,cAAA,CAAe,WAAA;EAAf;;;EA4FL,MAAA,CAAO,IAAA,UAAc,EAAA,UAAY,KAAA,EAAO,kBAAA,GAAqB,OAAA,CAAQ,WAAA;EAAR;;;EA8D7D,MAAA,CAAO,IAAA,UAAc,EAAA,WAAa,OAAA;EA4DzB;;;EA3CT,OAAA,CAAQ,IAAA,UAAc,EAAA,WAAa,OAAA;EA4CtC;;;EA2I0D;;;;;;;;;;EA7JvD,eAAA,CAAgB,IAAA,UAAc,EAAA,WAAa,OAAA;EAoYtC;;;EArXL,WAAA,CACL,IAAA,UACA,OAAA,GAAS,IAAA,CAAK,eAAA,aACZ,OAAA,CAAQ,cAAA,CAAe,WAAA;IAAgB,SAAA;EAAA;EAymBI;;;EAjiBxC,YAAA,CAAa,IAAA,WAAe,OAAA;EAroBd;;;;;;EAAA,QAupBZ,iBAAA;EAlpBK;;;;;;EAAA,QA6qBL,eAAA;EA1kB+D;;;EAgmBjE,KAAA,CAAM,IAAA,UAAc,KAAA,GAAQ,eAAA,YAA2B,OAAA;EAxiBnB;;;;;;EAykBpC,qBAAA,CAAsB,IAAA,WAAe,OAAA;EAiBrC,QAAA,CAAS,IAAA,WAAe,OAAA;IAAU,KAAA;IAAe,SAAA;IAAmB,KAAA;EAAA;EA3hBR;;;;;;EAqjB5D,QAAA,CAAS,IAAA,UAAc,EAAA,UAAY,WAAA,WAAsB,OAAA,CAAQ,WAAA;EArhBjE;;;;;;EAmkBA,UAAA,CAAW,IAAA,UAAc,EAAA,WAAa,OAAA,CAAQ,WAAA;EA5hB9C;;;;;;;;;;;;EAukBA,kBAAA,CAAmB,IAAA,UAAc,KAAA,YAAiB,OAAA,CAAQ,WAAA;EAzgB/D;;;EAmiBK,gBAAA,CAAiB,IAAA,UAAc,gBAAA,WAA2B,OAAA,CAAQ,WAAA;EAjiB7D;;;;;;;;;;;;;;;;;;;;;;EAokBL,OAAA,CACL,IAAA,UACA,EAAA,UACA,WAAA,WACA,UAAA,aACE,OAAA,CAAQ,WAAA;EAnXD;;;;;;EA+gBJ,SAAA,CAAU,IAAA,UAAc,EAAA,WAAa,OAAA,CAAQ,WAAA;EAtc7C;;;;;;;;;;;EA+fA,gBAAA,CAAiB,IAAA,UAAc,EAAA,UAAY,UAAA,WAAqB,OAAA;EA3Z3B;;;;;;EA6brC,YAAA,CAAa,IAAA,UAAc,EAAA,WAAa,OAAA,CAAQ,WAAA;EAlZhD;;;;;EAAA,QAqbQ,eAAA;EAvYR;;;;;EAoaA,cAAA,CAAe,IAAA,WAAe,OAAA;EAzXX;;;;;EAAA,QA0YjB,MAAA;EAhX6B;;;;EAAA,QAiZ7B,aAAA;AAAA;;;KCz2CG,WAAA;AAAA,UAEK,SAAA;EAChB,EAAA;EACA,QAAA;EACA,QAAA;EACA,IAAA;EACA,KAAA;EACA,MAAA;EACA,GAAA;EACA,OAAA;EACA,UAAA;EACA,MAAA,EAAQ,WAAA;EACR,WAAA;EACA,QAAA;EACA,aAAA;EACA,SAAA;EACA,QAAA;AAAA;AAAA,UAGgB,gBAAA;EAChB,QAAA;EACA,QAAA;EACA,IAAA;EACA,KAAA;EACA,MAAA;EACA,GAAA;EACA,OAAA;EACA,UAAA;EACA,WAAA;EACA,QAAA;EACA,aAAA;EACA,MAAA,GAAS,WAAA;EACT,QAAA;AAAA;AAAA,UAGgB,oBAAA;EAChB,KAAA;EACA,MAAA;;EAEA,QAAA;EACA,MAAA,GAAS,WAAA;;EAET,CAAA;AAAA;;;;cAMY,eAAA;EAAA,QACQ,EAAA;cAAA,EAAA,EAAI,MAAA,CAAO,QAAA;EDyOmB;;;ECpO5C,MAAA,CAAO,KAAA,EAAO,gBAAA,GAAmB,OAAA,CAAQ,SAAA;EDiRpC;;;ECnPL,aAAA,CAAc,KAAA;IACnB,QAAA;IACA,QAAA;IACA,IAAA;IACA,UAAA;IACA,WAAA;IACA,QAAA;EAAA,IACG,OAAA,CAAQ,SAAA;ED2VT;;;ECjVG,aAAA,CACL,EAAA,UACA,QAAA;IAAa,KAAA;IAAgB,MAAA;IAAiB,IAAA;EAAA,IAC5C,OAAA,CAAQ,SAAA;EDoiBD;;;EC/gBJ,UAAA,CAAW,EAAA,WAAa,OAAA,CAAQ,SAAA;EDwlBJ;;;EC1kB5B,QAAA,CAAS,EAAA,WAAa,OAAA,CAAQ,SAAA;ED+rBN;;;;ECjrBxB,cAAA,CAAe,QAAA,WAAmB,OAAA,CAAQ,SAAA;EDoyBgB;;;;ECtxB1D,iBAAA,CAAkB,WAAA,WAAsB,OAAA,CAAQ,SAAA;EDw1BnD;;;;;;ECv0BG,QAAA,CAAS,OAAA,GAAS,oBAAA,GAA4B,OAAA,CAAQ,cAAA,CAAe,SAAA;ED8nChC;;;EChkCrC,MAAA,CACL,EAAA,UACA,KAAA,EAAO,OAAA,CAAQ,IAAA,CAAK,gBAAA,6CAClB,OAAA,CAAQ,SAAA;EDzKa;;;EC+LlB,MAAA,CAAO,EAAA,WAAa,OAAA;ED1LN;;;ECmMd,KAAA,CAAM,QAAA,gCAAwC,OAAA;EDhG9C;;;;;;;ECmHA,qBAAA,CAAsB,QAAA,YAAoC,OAAA;ED3DtB;;;EAAA,QCqFlC,SAAA;AAAA;;;;;;;KClWG,QAAA;;KAGA,YAAA;AAAA,UAEK,IAAA;EAChB,EAAA;EACA,KAAA;EACA,IAAA;EACA,IAAA,EAAM,QAAA;EACN,SAAA;EACA,aAAA;EACA,IAAA,EAAM,MAAA;EACN,SAAA;AAAA;AAAA,UAGgB,eAAA;EAChB,KAAA;EACA,IAAA;EACA,IAAA,GAAO,QAAA,GAAW,YAAA;EAClB,SAAA;EACA,IAAA,GAAO,MAAA;AAAA;AAAA,UAGS,eAAA;EAChB,IAAA;EACA,IAAA,GAAO,QAAA,GAAW,YAAA;EAClB,SAAA;EACA,IAAA,GAAO,MAAA;AAAA;;;;cAMK,cAAA;EAAA,QACQ,EAAA;cAAA,EAAA,EAAI,MAAA,CAAO,QAAA;;AFyEhC;;EEpEO,MAAA,CAAO,KAAA,EAAO,eAAA,GAAkB,OAAA,CAAQ,IAAA;EFqEf;;;EE5CzB,QAAA,CAAS,EAAA,WAAa,OAAA,CAAQ,IAAA;EFiDK;;;;;EElCnC,SAAA,CAAU,GAAA,aAAgB,OAAA,CAAQ,IAAA;EF4P0B;;;EE7O5D,WAAA,CAAY,KAAA,WAAgB,OAAA,CAAQ,IAAA;EFiR/B;;;EEpQL,QAAA,CACL,OAAA;IACC,IAAA,GAAO,QAAA,GAAW,YAAA;IAClB,KAAA;IACA,MAAA;EAAA,IAEC,OAAA,CAAQ,cAAA,CAAe,IAAA;EFiWf;;;EE1TL,MAAA,CAAO,EAAA,UAAY,KAAA,EAAO,eAAA,GAAkB,OAAA,CAAQ,IAAA;EFsZS;;;EElY7D,MAAA,CAAO,EAAA,WAAa,OAAA;EF4fX;;;EEnfT,KAAA,CAAM,IAAA,GAAO,QAAA,GAAW,YAAA,GAAe,OAAA;EFof1C;;;EEteG,WAAA,CAAY,KAAA,WAAgB,OAAA;EFkpBS;;;EAAA,QEroBnC,SAAA;EF8tB4C;EAAA,wBEhtB5B,kBAAA;EF2vBwC;EAAA,wBElvBxC,YAAA;EF4wBgD;;;;EAAA,OEtwBjE,WAAA,CAAY,IAAA,EAAM,QAAA,GAAW,YAAA,GAAe,QAAA;EF08BR;;;;EAAA,eEx7B5B,MAAA;AAAA;;;UCtQC,QAAA;EAChB,EAAA;EACA,UAAA;EACA,OAAA;EACA,IAAA,EAAM,MAAA;EACN,QAAA;EACA,SAAA;AAAA;;;;UCoBgB,aAAA;EAChB,EAAA;EACA,QAAA;EACA,UAAA;EACA,gBAAA;EACA,IAAA;EACA,SAAA;EACA,OAAA,GAAU,aAAA;AAAA;;;;;;;;cCNE,uBAAA,yBAAgD,iBAAA,CAAkB,CAAA;EAAA,QAIrE,EAAA;EAAA,QACA,QAAA;EAAA,QACA,UAAA;EAAA,QALD,aAAA;cAGC,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,QAAA,UACA,UAAA,UACR,OAAA,EAAS,KAAA;EPpC8B;;;EO4ClC,GAAA,CAAI,EAAA,WAAa,OAAA,CAAQ,CAAA;EPzCd;;;EO0DX,GAAA,CAAI,EAAA,UAAY,IAAA,EAAM,CAAA,GAAI,OAAA;;;ANzDjC;EMmFO,MAAA,CAAO,EAAA,WAAa,OAAA;;;;EAcpB,MAAA,CAAO,EAAA,WAAa,OAAA;;;ALe3B;EKAO,OAAA,CAAQ,GAAA,aAAgB,OAAA,CAAQ,GAAA,SAAY,CAAA;;;;EAsB5C,OAAA,CAAQ,KAAA,EAAO,KAAA;IAAQ,EAAA;IAAY,IAAA,EAAM,CAAA;EAAA,KAAO,OAAA;EL2IgB;;;EKzGhE,UAAA,CAAW,GAAA,aAAgB,OAAA;ELwKiC;;;EKxJ5D,KAAA,CAAM,OAAA,GAAS,YAAA,GAAoB,OAAA,CAAQ,eAAA;IAAkB,EAAA;IAAY,IAAA,EAAM,CAAA;EAAA;EL+NtB;;;EK5IzD,KAAA,CAAM,KAAA,GAAQ,WAAA,GAAc,OAAA;AAAA;;;;;;KC7QvB,UAAA;;;;;;UAOK,eAAA;EAChB,IAAA;;;;EAIA,UAAA,EAAY,UAAA;EACZ,MAAA,EAAQ,CAAA,CAAE,UAAA;EACV,OAAA;EACA,EAAA,GAAK,YAAA;EACL,UAAA,GAAa,eAAA;AAAA;;;;UAMG,YAAA;EAChB,MAAA;EACA,WAAA;EACA,QAAA;EACA,IAAA;EACA,GAAA;EACA,GAAA;EAAA,CACC,GAAA;AAAA;;;;UAMe,mBAAA;EAChB,KAAA;EACA,IAAA;EAAA,CACC,GAAA;AAAA;;;;KAUU,UAAA,GAAa,UAAA;;;;UAKR,SAAA;EAChB,EAAA;EACA,GAAA;EACA,QAAA;EACA,QAAA;EACA,IAAA;AAAA;;;UCrDgB,YAAA;EAChB,QAAA;EACA,OAAA;EACA,YAAA;AAAA;AAAA,iBAGe,KAAA,CAAM,OAAA,GAAS,YAAA,GAAoB,eAAA,CAAgB,UAAA;;;UCdlD,WAAA;EAChB,QAAA;EACA,OAAA;EACA,YAAA;EACA,QAAA;AAAA;AAAA,iBAGe,IAAA,CAAK,OAAA,GAAS,WAAA,GAAmB,eAAA,CAAgB,SAAA;;;;;;;iBCHjD,SAAA,CACf,UAAA,UACA,OAAA;EACC,QAAA;AAAA,IAEC,eAAA;;;;;;;iBCKa,YAAA,CAAa,OAAA;EAC5B,QAAA;AAAA,IACG,eAAA,CAAgB,mBAAA;;;;;;UC4QH,kBAAA;EAChB,EAAA;EACA,IAAA;EACA,IAAA;EACA,MAAA;EACA,IAAA,EAAM,MAAA;EACN,QAAA;EACA,SAAA;EACA,SAAA;EACA,WAAA;EACA,SAAA;AAAA;;;;iBAoDqB,iBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,MAAA;EACC,MAAA;EACA,KAAA;EACA,MAAA;EACA,OAAA;EACA,KAAA;EACA,MAAA;EACA,CAAA;EACA,QAAA;EACA,SAAA,GAAY,gBAAA;EACZ,QAAA;EACA,MAAA;AAAA,IAEC,OAAA,CAAQ,SAAA,CAAU,mBAAA;;UAiFJ,aAAA;EAChB,EAAA;EACA,IAAA;EACA,KAAA;EACA,SAAA;AAAA;;;;;;;;;iBAWqB,oBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,WACE,OAAA,CAAQ,SAAA;EAAY,KAAA,EAAO,aAAA;AAAA;;;;iBAwCR,gBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,EAAA,UACA,MAAA,YACE,OAAA,CAAQ,SAAA,CAAU,eAAA;;;;;iBAwCC,gCAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,EAAA,UACA,MAAA,YACE,OAAA,CAAQ,SAAA,CAAU,eAAA;;;;;;;;iBA2CC,mBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,IAAA;EACC,IAAA,EAAM,MAAA;EACN,IAAA;EACA,MAAA;EACA,QAAA;EACA,OAAA,GAAU,kBAAA;EACV,MAAA;EACA,aAAA;EACA,GAAA,GAAM,eAAA;EACN,SAAA;EACA,WAAA;AAAA,IAEC,OAAA,CAAQ,SAAA,CAAU,eAAA;;;;;;;;iBAkKC,mBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,EAAA,UACA,IAAA;EACC,IAAA,GAAO,MAAA;EACP,IAAA;EACA,MAAA;EACA,QAAA;EACA,OAAA,GAAU,kBAAA;EACV,MAAA;EACA,IAAA;EACA,GAAA,GAAM,eAAA;EACN,WAAA;AAAA,IAEC,OAAA,CAAQ,SAAA,CAAU,eAAA;;;;;;;iBAyLC,sBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,EAAA,UACA,QAAA,YACE,OAAA,CAAQ,SAAA;EAAY,IAAA,EAAM,WAAA;AAAA;;;;iBAgEP,mBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,EAAA,WACE,OAAA,CAAQ,SAAA;EAAY,OAAA;AAAA;;;;iBAqCD,oBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,EAAA,WACE,OAAA,CAAQ,SAAA;EAAY,QAAA;AAAA;;;;;iBAsCD,4BAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,EAAA,WACE,OAAA,CAAQ,SAAA;EAAY,OAAA;AAAA;;;;iBAsDD,wBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,OAAA;EAAW,KAAA;EAAgB,MAAA;AAAA,IACzB,OAAA,CAAQ,SAAA;EAAY,KAAA,EAAO,kBAAA;EAAsB,UAAA;AAAA;;;;iBA+C9B,yBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,WACE,OAAA,CAAQ,SAAA;EAAY,KAAA;AAAA;;;;iBAwBD,qBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,EAAA,UACA,WAAA,WACE,OAAA,CAAQ,SAAA,CAAU,eAAA;;;;iBAuCC,uBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,EAAA,WACE,OAAA,CAAQ,SAAA,CAAU,eAAA;;;;;;;;iBA2CC,oBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,EAAA,UACA,OAAA;EAAW,WAAA;EAAsB,mBAAA;AAAA,IAC/B,OAAA,CAAQ,SAAA,CAAU,eAAA;;;;;;;iBAqDC,sBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,EAAA,WACE,OAAA,CAAQ,SAAA,CAAU,eAAA;;;;iBAuCC,2BAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,WACE,OAAA,CAAQ,SAAA;EAAY,KAAA;AAAA;;;;iBAwBD,yBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,EAAA,WACE,OAAA,CAAQ,SAAA,CAAU,eAAA;;;;iBAuCC,oBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,EAAA,WACE,OAAA,CACF,SAAA;EACC,UAAA;EACA,IAAA,EAAM,MAAA;EACN,KAAA,EAAO,MAAA;AAAA;;;;;iBA+Ca,yBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,EAAA,WACE,OAAA,CACF,SAAA;EACC,gBAAA;EACA,YAAA,EAAc,KAAA;IACb,EAAA;IACA,MAAA;IACA,IAAA;IACA,MAAA;IACA,SAAA;EAAA;AAAA;;;UC5iDO,oBAAA;EACT,MAAA;IACC,IAAA;MAAS,KAAA,SAAc,MAAA;IAAA;IACvB,KAAA,GAAQ,MAAA;EAAA;EAET,KAAA;IACC,KAAA;IACA,aAAA;IACA,QAAA;EAAA;AAAA;AAAA,KAGG,aAAA,GAAgB,MAAA,SAAe,oBAAA;AdlBpC;;;AAAA,iBcuBsB,gBAAA,CACrB,WAAA,EAAa,aAAA,EACb,OAAA,GAAS,MAAA;EAGP,UAAA,GAAa,KAAA;IAAQ,IAAA;IAAc,SAAA;EAAA;EACnC,OAAA;AAAA,KAGA,OAAA,CAAQ,gBAAA;;;UCzBM,oBAAA;EAChB,KAAA,EAAO,QAAA;EACP,KAAA;AAAA;AAAA,UAGgB,gBAAA;EAChB,IAAA,EAAM,QAAA;AAAA;;;;iBAMe,kBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,OAAA,UACA,MAAA;EAAU,KAAA;AAAA,IACR,OAAA,CAAQ,SAAA,CAAU,oBAAA;;;;iBA0BC,iBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,WACE,OAAA,CAAQ,SAAA,CAAU,gBAAA;;;;iBAiCC,qBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,YAAA,WACE,OAAA,CAAQ,SAAA,CAAU,eAAA;;;UCpFJ,iBAAA;EAChB,KAAA,EAAO,SAAA;EACP,UAAA;AAAA;AAAA,UAGgB,aAAA;EAChB,IAAA,EAAM,SAAA;AAAA;;;;iBAMe,eAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,MAAA;EACC,MAAA;EACA,KAAA;EACA,QAAA;EACA,CAAA;AAAA,IAEC,OAAA,CAAQ,SAAA,CAAU,iBAAA;;;;iBAqCC,cAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,EAAA,WACE,OAAA,CAAQ,SAAA,CAAU,aAAA;;;;iBAiCC,iBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,KAAA;EACC,QAAA;EACA,QAAA;EACA,IAAA;EACA,KAAA;EACA,MAAA;EACA,GAAA;EACA,UAAA;EACA,WAAA;EACA,QAAA;EACA,aAAA;EACA,QAAA;AAAA,IAEC,OAAA,CAAQ,SAAA,CAAU,aAAA;;;;iBAuBC,iBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,EAAA,UACA,KAAA;EACC,GAAA;EACA,OAAA;EACA,KAAA;EACA,MAAA;AAAA,IAEC,OAAA,CAAQ,SAAA,CAAU,aAAA;;;;iBAiCC,iBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,EAAA,WACE,OAAA,CAAQ,SAAA;EAAY,OAAA;AAAA;;;;;;cCvGV,WAAA,SAAoB,KAAA;EAGxB,IAAA;EACA,OAAA,GAAU,MAAA;cAFjB,OAAA,UACO,IAAA,UACA,OAAA,GAAU,MAAA;AAAA;;;;;;;cAaN,cAAA;EAAA,QACQ,EAAA;cAAA,EAAA,EAAI,MAAA,CAAO,QAAA;;;;EASzB,eAAA,CAAA,GAAmB,OAAA,CAAQ,UAAA;;;;EAa3B,aAAA,CAAc,IAAA,WAAe,OAAA,CAAQ,UAAA;EjBpHX;;;EiBiI1B,uBAAA,CAAwB,IAAA,WAAe,OAAA,CAAQ,oBAAA;EhBhIrC;;;;;;;;ACgHjB;;;;;;;;EeyCO,yBAAA,CAAA,GAA6B,OAAA,CAAQ,oBAAA;EfwH2B;;;Ee9EhE,gBAAA,CAAiB,KAAA,EAAO,qBAAA,GAAwB,OAAA,CAAQ,UAAA;Ef6II;;;EehF5D,gBAAA,CAAiB,IAAA,UAAc,KAAA,EAAO,qBAAA,GAAwB,OAAA,CAAQ,UAAA;EfoHjE;;;Ee/BL,gBAAA,CAAiB,IAAA,UAAc,OAAA;IAAY,KAAA;EAAA,IAAoB,OAAA;EfiI3D;;;Ee3FJ,UAAA,CAAW,YAAA,WAAuB,OAAA,CAAQ,KAAA;EfwLF;;;EezKxC,QAAA,CAAS,cAAA,UAAwB,SAAA,WAAoB,OAAA,CAAQ,KAAA;EfwP1B;;;EevOnC,WAAA,CAAY,cAAA,UAAwB,KAAA,EAAO,gBAAA,GAAmB,OAAA,CAAQ,KAAA;EfmRlD;;;EepLpB,WAAA,CACL,cAAA,UACA,SAAA,UACA,KAAA,EAAO,gBAAA,GACL,OAAA,CAAQ,KAAA;Ef2TuB;;;;;;;;;;;;;;EAAA,QenOpB,eAAA;EfsmB6B;;;Ee3kBrC,WAAA,CAAY,cAAA,UAAwB,SAAA,WAAoB,OAAA;EfsuB1B;;;EexsB9B,aAAA,CAAc,cAAA,UAAwB,UAAA,aAAuB,OAAA;;;;UAwBrD,kBAAA;EfjjBR;;;EAAA,Qe0pBQ,SAAA;Ef1pBmC;;;EAAA,QegsBnC,UAAA;Ef7lBuC;;;EAAA,QeknBvC,oBAAA;Ef1jBgB;;;EAAA,Qe2kBtB,YAAA;EfjiBF;;;EAAA,QeyiBE,aAAA;EfziB0C;;;EAAA,QeijB1C,YAAA;Ef5hBkD;;;;;;;;;;;EAAA,QewjBlD,kBAAA;EfphBL;;;EAAA,QeqkBK,eAAA;EfliBS;;;EAAA,QeojBT,gBAAA;EfpjB+D;;;EAAA,QeglB/D,WAAA;Ef/iBP;;;;;;Ee8kBK,sBAAA,CAAA,GAA0B,OAAA,CAC/B,KAAA;IAAQ,IAAA;IAAc,SAAA;IAAmB,QAAA;EAAA;Efpd7B;;;;;EeygBP,qBAAA,CACL,IAAA,UACA,OAAA;IACC,KAAA;IACA,aAAA;IACA,WAAA;EAAA,IAEC,OAAA,CAAQ,UAAA;EfjcL;;;EAAA,Qe+eE,WAAA;AAAA;;;;;;;;;;;;;AjBrnCT;;;iBkBsBsB,iBAAA,CAAkB,IAAA,WAAe,OAAA,CAAQ,UAAA;;;;;;KChBnD,aAAA;;;;UAKK,OAAA;EAChB,EAAA;EACA,IAAA;EACA,KAAA;EACA,WAAA;EACA,QAAA;EACA,OAAA,EAAS,mBAAA;EACT,UAAA;EACA,MAAA,EAAQ,aAAA;EACR,OAAA;EACA,SAAA;EACA,SAAA;AAAA;;;;UAuBgB,kBAAA;EAChB,IAAA;EACA,KAAA;EACA,WAAA;EACA,QAAA;EACA,OAAA,EAAS,mBAAA;EACT,cAAA;EACA,MAAA,GAAS,aAAA;EACT,OAAA;AAAA;;;;UAMgB,kBAAA;EAChB,IAAA;EACA,KAAA;EACA,WAAA;EACA,QAAA;EACA,OAAA,GAAU,mBAAA;EACV,cAAA;AAAA;;;;UAMgB,kBAAA;EjB8iBwB;EiB5iBxC,MAAA,GAAS,aAAA;EjBulBwC;EiBrlBjD,MAAA;EjBsmBU;EiBpmBV,KAAA;EjBqmBW;EiBnmBX,MAAA;AAAA;;;;;;;;;;;AnB/ED;;;;;iBoB8BsB,UAAA,CAAW,IAAA,WAAe,OAAA,CAAQ,OAAA;;;;AlBsFxD;;;;;;;;;;;iBkBvBsB,WAAA,CACrB,OAAA,GAAS,kBAAA,GACP,OAAA,CAAQ,cAAA,CAAe,OAAA;;;;;;;UClFT,cAAA;;EAEhB,KAAA;ErBfY;EqBiBZ,QAAA;;EAEA,WAAA;ErBnBwC;EqBqBxC,UAAA;AAAA;;;;;UAOgB,iBAAA;;EAEhB,GAAA,CAAI,IAAA,WAAe,OAAA;EpB1BH;EoB4BhB,MAAA,CAAO,IAAA,WAAe,OAAA;AAAA;;;;;UAON,mBAAA;EAChB,EAAA;EACA,OAAA;EACA,IAAA;EACA,IAAA;AAAA;;;;;;;;KAUW,wBAAA,IACX,OAAA,EAAS,mBAAA,EACT,QAAA,aACI,OAAA;;;;UAKY,cAAA;EnB2TL;EmBzTX,OAAA,GAAU,iBAAA;EnB4V6D;EmB1VvE,EAAA,EAAI,MAAA,CAAO,QAAA;EnB4XA;EmB1XX,MAAA,GAAS,cAAA;EnBuZC;EmBrZV,QAAA;IAAa,IAAA;IAAc,GAAA;IAAa,MAAA;EAAA;EnBkf2B;EmBhfnE,SAAA,GAAY,wBAAA;EnB+jB6B;;;;;EmBzjBzC,YAAA;IACC,MAAA,CAAO,OAAA;MAAW,GAAA;MAAa,IAAA,EAAM,UAAA;MAAY,WAAA;IAAA,IAAwB,OAAA;IACzE,MAAA,CAAO,GAAA,WAAc,OAAA;EAAA;AAAA;;;;;;;;UAWN,uBAAA;EnBsmCmC;EAAA,SmBpmC1C,EAAA;EnB6pC6D;;;;;;;EmBppCtE,UAAA,CAAW,QAAA,UAAkB,KAAA,YAAiB,OAAA;EnBgBtB;;;;;;;;EmBNxB,WAAA,CAAY,SAAA,UAAmB,KAAA,WAAgB,OAAA,EAAS,iBAAA,GAAoB,OAAA;EnB8GnD;;;;EmBxGzB,SAAA,IAAa,OAAA;AAAA;;;;;UAOG,iBAAA;EAChB,GAAA;EACA,MAAA;EACA,OAAA,EAAS,MAAA;EnBgMyC;EmB9LlD,IAAA,EAAM,WAAA;AAAA;;;;;UAOU,aAAA;EnBkOf;;;;EmB7ND,WAAA;EnBwOC;;;;;;EmBhOD,SAAA;EnBsQiB;;;;;;;;EmB5PjB,IAAA,CAAK,QAAA,EAAU,cAAA,EAAgB,IAAA,WAAe,OAAA,CAAQ,uBAAA;EnB8RnD;;;;;EmBvRH,YAAA,CAAa,QAAA,EAAU,wBAAA;EnBqTpB;;;;EmB/SH,YAAA,IAAgB,OAAA;AAAA;;;;;cAOJ,uBAAA,SAAgC,KAAA;cAChC,QAAA,UAAkB,MAAA;AAAA;;;;;;;;;;;;;KAkBnB,oBAAA,IAAwB,OAAA,EAAS,cAAA,KAAmB,aAAA;;;;;;;;;;;;;;;;;;;UC1L/C,cAAA;;AtBZjB;;;;;;;;;EsBuBC,aAAA;EtBpBgC;;;;ACCjC;;;;;;;EqBgCC,cAAA;EpBgFY;;;;;;;EoBvEZ,MAAA;IpBgLuE;;;;;;;;;;;;;;;;;;;IoB5JtE,iBAAA;IpBsf0E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IoBhd1E,wBAAA;EAAA;AAAA;;;;;;;;;;;;;;;;;;;;;KAwBU,mBAAA,YAA+B,cAAA;;;;;;;;UAS1B,kBAAA;EpBoOb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EoBrKH,QAAA,GAAW,mBAAA;AAAA;;;;;;;;;;;UCnMK,gBAAA;EAChB,KAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;AAAA;;;;UAMgB,mBAAA;EAChB,KAAA;EACA,IAAA;EAAA,CACC,GAAA;AAAA;;;;UAMe,oBAAA,SAA6B,mBAAA;EAC7C,KAAA;EACA,IAAA;EACA,KAAA;AAAA;;;AtBtBD;UsB4BiB,qBAAA;EAChB,KAAA;EACA,IAAA;EACA,KAAA;EACA,QAAA;EACA,KAAA;EACA,QAAA,EAAU,gBAAA;EACV,QAAA,GAAW,mBAAA;AAAA;;;;UAMK,sBAAA;EAChB,KAAA;EACA,IAAA;EACA,KAAA;IACC,IAAA;IACA,GAAA,WrB6QiD;IqB3QjD,QAAA;EAAA;EAED,GAAA;EACA,OAAA;ErBqTG;EqBnTH,KAAA;ErB+TG;EqB7TH,MAAA;ErBgW+D;EqB9V/D,YAAA;ErBgYG;EqB9XH,aAAA;AAAA;;;;UAMgB,qBAAA;EAChB,KAAA;EACA,IAAA;EACA,IAAA;EACA,QAAA;EACA,QAAA;AAAA;;;;UAMgB,qBAAA;EAChB,KAAA;EACA,IAAA;EACA,IAAA;AAAA;;;;UAMgB,wBAAA;EAChB,KAAA;EACA,IAAA;EAAA,CACC,GAAA;AAAA;;;;KAMU,iBAAA,GACT,qBAAA,GACA,sBAAA,GACA,qBAAA,GACA,qBAAA,GACA,wBAAA;;;;UAKc,eAAA;EAChB,IAAA;EACA,KAAA,GAAQ,MAAA;AAAA;AAAA,UAGQ,eAAA;EAChB,IAAA;EACA,KAAA,GAAQ,MAAA;EACR,OAAA,GAAU,eAAA;EACV,KAAA,GAAQ,eAAA;EACR,IAAA;AAAA;AAAA,UAGgB,mBAAA;EAChB,IAAA;EACA,OAAA,EAAS,eAAA;AAAA;;;;;;iBCnGM,yBAAA,CAA0B,GAAA,EAAK,mBAAA,GAAsB,iBAAA;;;;;;iBCRrD,yBAAA,CAA0B,MAAA,EAAQ,iBAAA,KAAsB,mBAAA;;;;;;;iBCjBlD,UAAA,CAAW,OAAA,WAAkB,OAAA;;;;;;;;;;iBAgB7B,kBAAA,CAAmB,OAAA,EAAS,UAAA,GAAa,WAAA,GAAc,OAAA;;;;;;;;;;;;;;;;;;;;;A1Bf7E;;;;;iB2BgCgB,YAAA,CAAa,GAAA;;;;iBAQb,UAAA,CAAW,GAAA;;;;;;KCmSf,SAAA,GAAY,MAAA;;;;KAKZ,eAAA;A3B5UZ;;;;;AAAA,K2BmVY,aAAA,GAAc,MAAA,SAAe,eAAA;;;A1BnOzC;;;;U0B4YiB,UAAA;EAChB,EAAA;EACA,GAAA;EACA,EAAA;EACA,GAAA;AAAA;;;;KAMW,UAAA,uBAAiC,UAAA;;;;UAK5B,kBAAA;EAChB,IAAA;EACA,MAAA;EACA,KAAA;E1BrFW;;;;E0B0FX,MAAA;E1B5DG;;;;;;;;;;;;;;E0B2EH,KAAA,GAAQ,MAAA,SAAe,UAAA;E1BwToB;;;;E0BnT3C,OAAA,GAAU,aAAA;E1B4YkC;;;;E0BvY5C,MAAA;AAAA;;;;UAMgB,WAAA;EAChB,IAAA;EACA,EAAA;E1BmyBoC;;;;E0B9xBpC,UAAA;E1BxcwB;;;;E0B6cxB,MAAA;AAAA;;;;;;;;;;;iBAgBqB,KAAA,CAAA,GAAS,OAAA,CAAQ,MAAA,CAAO,QAAA;;;;;;;;;;;;;;;;;;;;;;;iBA0C9B,YAAA,CAAA,GAAgB,UAAA,CAAW,SAAA,EAAW,WAAA,EAAa,kBAAA;;;;;;;;UC3nBlD,aAAA;EAChB,UAAA;EACA,EAAA;EACA,KAAA;;EAEA,MAAA;;EAEA,QAAA;AAAA;;UAIgB,eAAA;EAChB,iBAAA;AAAA;AAAA,UAGgB,eAAA;E7BhBgB;E6BkBhC,MAAA;E7BlB6C;E6BoB7C,QAAA;AAAA;;;;;;;;;iBAWe,cAAA,CACf,UAAA,UACA,EAAA,UACA,OAAA,GAAU,eAAA,GACR,SAAA;;;;;iBAyCa,UAAA,CAAA,GAAc,SAAA;;;A3BwC9B;;;;;;K2BhBY,SAAA;EAAA,UACD,KAAA,WAAgB,OAAA,CAAQ,eAAA;AAAA;;;;;;;;;A5BjGnC;;;;;;;;ACgHA;;;;;;;U4B5DiB,iBAAA;;;;;KAML,mBAAA,qBAAwC,CAAA,eAAgB,iBAAA,GACjE,iBAAA,CAAkB,CAAA,IAClB,MAAA;;;;KAKS,aAAA;;;;;;;KAQA,WAAA,GAAc,MAAA,SAAe,aAAA;AAAA,UAIxB,gBAAA;EAChB,MAAA;EACA,KAAA;E5BoemE;;;;;;;;;;;;E4BvdnE,MAAA;E5B+wB8B;;;;;;;;;;;;;;;;;E4B7vB9B,KAAA,GAAQ,MAAA,SAAe,UAAA;E5ByuCoB;;;;;;;E4BjuC3C,OAAA,GAAU,WAAA;E5BAG;;;;;;E4BOb,MAAA;AAAA;AAAA,UAGgB,YAAA,KAAiB,MAAA;EACjC,EAAA;EACA,IAAA,EAAM,CAAA;E5B+IoC;E4B7I1C,IAAA,EAAM,SAAA;AAAA;;UAIU,SAAA;EAChB,IAAA;EACA,YAAA,GAAe,IAAA;AAAA;;;;UAMC,gBAAA;E5BgMkD;E4B9LlE,OAAA,EAAS,YAAA,CAAa,CAAA;E5BmNrB;E4BjND,KAAA,GAAQ,KAAA;E5BmNP;E4BjND,SAAA,EAAW,SAAA;E5BkNA;;;;;E4B5MX,UAAA;AAAA;;;;UAMgB,WAAA;E5BqP6B;E4BnP7C,KAAA,EAAO,YAAA,CAAa,CAAA;E5BmPmD;E4BjPvE,KAAA,GAAQ,KAAA;E5BgRP;E4B9QD,SAAA;E5BgRC;E4B9QD,cAAA;E5B+QW;E4B7QX,SAAA,EAAW,SAAA;AAAA;;UASK,aAAA;EAChB,UAAA;EACA,EAAA;EACA,KAAA;AAAA;;;;;;iBAiBe,WAAA,CAAY,KAAA,YAAiB,aAAA;;;;;;;;;;;;;;;;;;;;;;;;iBAmGvB,mBAAA,uBAA0C,mBAAA,CAAoB,CAAA,EAAA,CACnF,IAAA,EAAM,CAAA,EACN,MAAA,GAAS,gBAAA,GACP,OAAA,CAAQ,gBAAA,CAAiB,CAAA;;;;;;;;;;;;;;;;;;;;;iBA4QN,cAAA,uBAAqC,mBAAA,CAAoB,CAAA,EAAA,CAC9E,IAAA,EAAM,CAAA,EACN,EAAA,UACA,OAAA;EAAY,MAAA;AAAA,IACV,OAAA,CAAQ,WAAA,CAAY,CAAA;;;;UAsRN,kBAAA;E5BuWA;E4BrWhB,EAAA;E5BqW2C;E4BnW3C,MAAA;E5B4ZM;E4B1ZN,IAAA;E5B0ZqC;E4BxZrC,MAAA;AAAA;;;;UAMgB,kBAAA;E5BobsC;E4BlbtD,gBAAA;E5BkfM;E4BhfN,YAAA,EAAc,kBAAA;E5BgfsB;E4B9epC,KAAA,GAAQ,KAAA;AAAA;;;;;;A3Bz0BT;;;;;AAEA;;;;;iB2By1BsB,eAAA,CAAgB,IAAA,UAAc,EAAA,WAAa,OAAA,CAAQ,kBAAA;;;;UAyCxD,iBAAA,KAAsB,MAAA;E3B33BtC;E2B63BA,KAAA,EAAO,YAAA,CAAa,CAAA;E3B33BpB;E2B63BA,UAAA;E3B53BQ;E2B83BR,MAAA,EAAQ,MAAA;AAAA;A3Bt3BT;;;;;;;;;;;;;;;;;;;AAAA,iB2By6BsB,iBAAA,KAAsB,MAAA,kBAAA,CAC3C,IAAA,WACE,OAAA,CAAQ,iBAAA,CAAkB,CAAA;;;;;;;;;;;;;;;;;;;;KCt9BjB,WAAA,IAAe,OAAA,EAAS,OAAA;A/BbpC;;;;;;;AAAA,iB+ByCgB,KAAA,CAAM,EAAA,eAAiB,OAAA;;;;;;;;;UCvCtB,UAAA;EAChB,aAAA;EACA,OAAA;EACA,QAAA,GAAW,MAAA;EACX,mBAAA;AAAA;;AhCND;;;iBgCuBgB,aAAA,CAAA,GAAiB,UAAA;;;;;iBAQjB,aAAA,CAAA;;;;;;iBASA,gBAAA,CAAiB,MAAA;;;;;;;;;AhCxCjC;;;;;;;;;;;;;;ACIA;;;;;;;;ACgHA;;;;;;;;;;;;iB+BpDgB,YAAA,kBAA8B,mBAAA,CAAA,CAC7C,UAAA,EAAY,gBAAA,CAAiB,QAAA,IAC3B,cAAA,CAAe,QAAA;;;;;;UCvDD,iBAAA;;EAEhB,UAAA;;EAEA,MAAA;AAAA;;;;UAgBgB,eAAA;EjC3Bc;EiC6B9B,QAAA;EjC5BA;EiC8BA,MAAA;;;;AhCiFD;;EgC3EC,WAAA;EhC4E+B;;;;;EgCtE/B,eAAA;EhCsOsE;EgCpOtE,MAAA;EhC8QkD;EgC5QlD,SAAA;AAAA;;;;UAMgB,kBAAA;EhC+Tb;EgC7TH,SAAA;EhCgW+D;EgC9V/D,OAAA;AAAA;;;;AlC7DD;;UmCmBiB,eAAA;EAChB,IAAA;EACA,KAAA;EACA,IAAA;AAAA;;;;UAMgB,qBAAA;EAChB,EAAA;EACA,IAAA;EACA,KAAA;AAAA;;;;;;;;AjCqFD;;;;;;;;;;;;;;;;UiC3DiB,4BAAA;EAChB,OAAA;EACA,aAAA;AAAA;AAAA,UAGgB,gBAAA,YAA4B,MAAA;EjC+XjC;EiC7XX,EAAA;EjC0ZU;EiCxZV,OAAA;EjCyZW;EiCvZX,UAAA;EjCmf8C;;;;;EiC7e9C,OAAA,GAAU,QAAA;EjCumBK;;;;;;;;;;;EiC3lBf,MAAA;EjCi2BoD;EiC/1BpD,UAAA;EjC04BgE;EiCx4BhE,eAAA;EjCk6BwE;EiCh6BxE,UAAA,GAAa,eAAA;EjCw8BF;EiCt8BX,YAAA,GAAe,qBAAA;EjCkmCoC;;;;;EiCzlCnD,YAAA;EjCovC2C;;;;EiC/uC3C,YAAA;EjCS+B;;;;EiCJ/B,OAAA,GAAU,MAAA,SAAe,4BAAA;AAAA;;;;;;;KASd,yBAAA,YAAqC,MAAA,qBAChD,gBAAA,CAAiB,QAAA;AAAA,UAED,YAAA;EjCwJc;;;;;;;;;;;;;;;;;EiCtI9B,QAAA,GAAW,kBAAA;EjC6NR;;;EiCzNH,OAAA,GAAU,iBAAA;EjCmOT;;;;;;;;;;;;;;;;EiClND,OAAA,GAAU,gBAAA;EjCoTJ;;;;;;;;;;;;;;;;;EiClSN,SAAA,GAAY,yBAAA;EjCgdN;;;;;;;;;;;EiCpcN,aAAA;EjC+eC;;;;;;;;;EiCpeD,OAAA;EjCgnBM;;;;;;;;;;;;;;;;;;;;;;;;EiCtlBN,IAAA,GAAO,cAAA;EjC2vBD;;;;;;;;;;;;;;;;EiCzuBN,aAAA,GAAgB,sBAAA;EjCu8BV;;;;;;;;;;;;;EiCx7BN,GAAA;EjCmhCsD;;;;;;;;;;;;ACvvCvD;;;;;AAEA;;;;EgCyPC,WAAA;EhCvPA;;;;;;;;;;;;;;;;;AAgBD;;;EgC6PC,YAAA,GAAe,kBAAA;EhC5Pf;;;;;;;;;;;EgCyQA,aAAA;EhC9PS;;;;AAIV;;;;;;;;;EgCyQC,OAAA;EhClQC;;AAMF;;;;;;;;;;;;;;;;;;;;;;;;;;;EgC2RC,cAAA;EAyBA,mBAAA;EhCrF0B;;;;;;;;;;;;;;;;;;;;;;;;EgC+G1B,UAAA;IhCzRM,iDgC2RL,oBAAA;EAAA;EhCzR6B;;;;;;;;;;;;;;;;;;;;;EgCiT9B,cAAA,GAAiB,uBAAA;EhChOO;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACrNzB;;;;;AAGA;;E+BudC,KAAA;I/BvduB;;AAExB;;;;;;;I+BieI,OAAA;EAAA;E/B5dH;;;;;;;AAMD;;;;;;;;;;;;E+B4eC,KAAA;I/BxeA,iF+B0eC,IAAA,W/BzeM;I+B2eN,QAAA,W/B3eY;I+B6eZ,OAAA;EAAA;;;;;;;EASD,YAAA;AAAA;;;;;iBAUe,eAAA,CAAA,GAAmB,YAAA;;;AlCzhBnC;;;;;;;;ACgHA;;ADhHA,cmC+Qa,oBAAA,EAAoB,GAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA2BrB,uBAAA,GAA0B,GAAA,CAAE,KAAA,QAAa,oBAAA;;;KC1PhD,UAAA;;;;UAuDY,UAAA;EAChB,OAAA;EACA,KAAA,GAAQ,CAAA;EACR,KAAA,GAAQ,KAAA;EACR,QAAA;EACA,QAAA;AAAA;;;;cAMY,YAAA;EAAA,QACJ,KAAA;EAAA,QACA,SAAA;EAAA,QACA,cAAA;;UAEA,qBAAA;EpCvHsB;EAAA,QoC0HtB,kBAAA;EpCzHR;;;;EAAA,QoC+HQ,mBAAA;cAEI,OAAA,EAAS,cAAA,IAAkB,cAAA,GAAiB,2BAAA;EnClB3B;;;;;;;;EmCsC7B,iBAAA,CAAkB,OAAA,EAAS,OAAA,CAAQ,2BAAA;EnC2H2B;;;EAAA,QmCjHtD,UAAA;EnCgLkD;;;;;;;;EAAA,QmC7JlD,aAAA;EnCmSE;;;;;;;EAAA,QmC7QF,eAAA;EnCybiC;;;;;;;EAAA,wBmClZjB,wBAAA;EnCykBU;;;EAAA,QmCziB1B,kBAAA;EnCqnB+D;;;EAAA,QmCzlB/D,YAAA;EnCkrBwD;;;EAAA,QmCzqBxD,SAAA;EnC2uBG;;;EAAA,QmCxsBG,kBAAA;EnC65BwD;;;EmCz4BhE,gBAAA,CAAiB,QAAA,WAAmB,OAAA,CAAQ,UAAA;EnC2+BP;;;EmCp+BrC,iBAAA,CAAkB,QAAA,WAAmB,OAAA,CAAQ,UAAA;EnClQ3B;;;EmCyQlB,mBAAA,CAAoB,QAAA,WAAmB,OAAA,CAAQ,UAAA;EnCpQjC;;;EmC2Qd,kBAAA,CAAmB,QAAA,UAAkB,UAAA,YAAsB,OAAA,CAAQ,UAAA;EAAA,QAgC3D,gBAAA;EnCxMW;;;;EmCmPnB,oBAAA,CACL,OAAA,EAAS,MAAA,mBACT,UAAA,UACA,KAAA,YACE,OAAA;IACF,OAAA,EAAS,MAAA;IACT,OAAA,EAAS,UAAA,CAAW,MAAA;EAAA;EnCjMyC;;;EmCiPxD,mBAAA,CACL,OAAA,EAAS,MAAA,mBACT,UAAA,UACA,KAAA,YACE,OAAA,CAAQ,UAAA;EnC3MkB;;;;EmCiPvB,sBAAA,CACL,EAAA,UACA,UAAA,WACE,OAAA;IAAU,OAAA;IAAkB,OAAA,EAAS,UAAA;EAAA;EnC3MlC;;;EmCsPA,qBAAA,CACL,EAAA,UACA,UAAA,UACA,SAAA,YACE,OAAA,CAAQ,UAAA;EnCtPR;;;EmC2RG,sBAAA,CACL,OAAA,EAAS,MAAA,mBACT,UAAA,WACE,OAAA,CAAQ,UAAA;EnCpRV;;;EmCyTK,wBAAA,CACL,OAAA,EAAS,MAAA,mBACT,UAAA,WACE,OAAA,CAAQ,UAAA;EnCtTG;;;EmC+VR,oBAAA,CAAqB,IAAA;IAAQ,IAAA;IAAc,IAAA;IAAc,IAAA;EAAA,IAAiB,OAAA;IAC/E,IAAA;MAAQ,IAAA;MAAc,IAAA;MAAc,IAAA;IAAA;IACpC,OAAA,EAAS,UAAA;MAAa,IAAA;MAAc,IAAA;MAAc,IAAA;IAAA;EAAA;EnClQzB;;;EmCkTpB,mBAAA,CAAoB,KAAA;IACzB,EAAA;IACA,QAAA;IACA,QAAA;IACA,IAAA;IACA,GAAA;IACA,SAAA;EAAA,IACG,OAAA,CAAQ,UAAA;EnC/J4B;;;;;;EmC2MlC,cAAA,CAAe,QAAA,UAAkB,KAAA,EAAO,SAAA,GAAY,OAAA,CAAQ,UAAA;EnChK9B;;;;;;;EmC6M9B,kBAAA,CACL,OAAA,EAAS,YAAA,EACT,MAAA,WACE,OAAA;IAAU,OAAA,EAAS,YAAA;IAAsB,OAAA,EAAS,UAAA,CAAW,YAAA;EAAA;EnCtH1D;;;;;EmCkLA,iBAAA,CAAkB,OAAA,EAAS,YAAA,EAAc,MAAA,WAAiB,OAAA,CAAQ,UAAA;EnC/G5D;;;;;;;EmC6JN,sBAAA,CACL,KAAA,EAAO,wBAAA,GACL,OAAA,CAAQ,wBAAA;EnC7GI;;;;;EmCuJT,qBAAA,CAAsB,KAAA,EAAO,uBAAA,GAA0B,OAAA;EnC7H9C;;;;;EmCoJT,uBAAA,CAAwB,KAAA,EAAO,yBAAA,GAA4B,OAAA;EnCtGhD;;;;EmCgIX,eAAA,CACL,KAAA,EAAO,iBAAA,GACL,OAAA,CAAQ,KAAA;IAAQ,QAAA;IAAkB,aAAA,EAAe,wBAAA;EAAA;EnCvFY;;;;EmCwH1D,gBAAA,CACL,KAAA,EAAO,iBAAA,GACL,OAAA,CAAQ,KAAA;IAAQ,QAAA;IAAkB,aAAA,EAAe,wBAAA;EAAA;EnC3DnD;;;EmC+FD,QAAA,CAAS,IAAA,EAAM,UAAA;EnC5FJ;;;EmCoGX,YAAA,CAAa,IAAA,EAAM,UAAA;EnCwDwB;;;EmCjD3C,kBAAA,CAAA,GAAsB,UAAA;EnC0Ge;;;EmC/FrC,2BAAA,CAAA;EnCiImB;;;EmC1HnB,eAAA,CAAgB,IAAA;EnC6JF;;;;EmCrJd,qBAAA,CAAsB,QAAA,UAAkB,QAAA;EnCoOhC;;;EmC7NR,uBAAA,CAAwB,QAAA;;;AlC5oCzB;EkCmpCC,qBAAA,CAAsB,QAAA;;;;EAOtB,yBAAA,CAA0B,QAAA,WAAmB,KAAA;IAAQ,QAAA;EAAA;ElC9oClC;;;;;;EkCypCnB,gBAAA,CAAiB,QAAA,WAAmB,KAAA;IAAQ,QAAA;EAAA;ElC1pC5C;;;;;;;;;;AASD;;EkCkqCO,mBAAA,CACL,QAAA,UACA,KAAA,YACE,OAAA;IAAU,MAAA;IAAiB,QAAA;IAAkB,KAAA,GAAQ,KAAA;IAAO,QAAA;EAAA;AAAA;;;;iBA4BhD,kBAAA,CACf,OAAA,EAAS,cAAA,IACT,cAAA,GAAiB,2BAAA,GACf,YAAA;;;;;;;ApCxvCH;;;;cqC8Da,aAAA;EAAA,QACJ,QAAA;cAEI,QAAA,EAAU,YAAA;;ApC+CvB;;;;;;EoCpCC,WAAA,CAAY,QAAA,EAAU,YAAA;EpC0CmB;;;;;;;;;EoC7BnC,IAAA,CAAK,OAAA,EAAS,YAAA,EAAc,MAAA,WAAiB,OAAA;EpC2RxC;;;EAAA,QoC/PG,SAAA;EpCoUH;;;;;;;EoCrPX,WAAA,CAAA;AAAA;;;;;;;;;iBCwVe,gBAAA,CAAiB,QAAA,UAAkB,YAAA,aAAyB,UAAA;;;;;;iBA2D5D,4BAAA,CAA6B,QAAA,WAAmB,UAAA;;;;UAsH/C,eAAA;ErCjDiC;EqCmDjD,QAAA;ErClCU;EqCoCV,OAAA;ErCnCW;EqCqCX,MAAA;AAAA;AAAA,UAwGgB,2BAAA;EAChB,EAAA,EAAI,MAAA,CAAO,QAAA;ErC2hBgC;;;;EqCthB3C,OAAA,GAAU,OAAA;ErChtBU;;;;EqCqtBpB,YAAA,IACC,QAAA,UACA,WAAA,aACI,OAAA;IAAU,SAAA;IAAmB,OAAA;EAAA;ErChnBK;;;;EqCqnBvC,QAAA,GAAW,eAAA;ErC7jBmB;;;;EqCkkB9B,cAAA;ErCxhBe;;;;EqC6hBf,aAAA,GAAgB,aAAA;ErCxgBe;;;;;;EqC+gB/B,mBAAA;AAAA;;;AvCn2BD;;;AAAA,UwCoBiB,WAAA;EAChB,OAAA;EACA,IAAA,GAAO,CAAA;EACP,KAAA;IACC,IAAA;IACA,OAAA;IACA,OAAA;EAAA;EAED,MAAA;AAAA;;AvCxBD;;UuC8BiB,kBAAA;EvC7BhB;EuC+BA,OAAA,EAAS,OAAA;;EAET,IAAA;AAAA;;;;;cA+HY,gBAAA,SAAyB,KAAA;EAE7B,IAAA;EAEA,MAAA;EACA,OAAA;cAHA,IAAA,UACP,OAAA,UACO,MAAA,WACA,OAAA;EtCwgBkB;;;EAAA,OsC/fnB,UAAA,CAAW,OAAA,UAAiB,OAAA,aAAoB,gBAAA;EtC0oBrB;;;EAAA,OsCnoB3B,YAAA,CAAa,OAAA,YAAmC,gBAAA;EtC+sBgB;;;EAAA,OsCxsBhE,SAAA,CAAU,OAAA,YAAgC,gBAAA;EtCiyBe;;;EAAA,OsC1xBzD,QAAA,CAAS,OAAA,YAAgC,gBAAA;EtC41BrC;;;EAAA,OsCr1BJ,QAAA,CAAS,OAAA,UAAiB,OAAA,aAAoB,gBAAA;EtC0iCiB;;;EAAA,OsCniC/D,QAAA,CAAS,OAAA,YAAqC,gBAAA;AAAA;;;;AxCtNtD;;KyCoCY,WAAA;;;;UAaK,oBAAA;EzC/Cf;EyCiDD,EAAA,EAAI,MAAA,CAAO,QAAA;EzChDqB;EyCkDhC,OAAA,GAAU,OAAA;;EAEV,YAAA,IACC,QAAA,UACA,WAAA,aACI,OAAA;IAAU,SAAA;IAAmB,OAAA;EAAA;;;;;EAKlC,mBAAA;AAAA;AvCqDD;;;;;AAAA,cuC7Ca,aAAA;EAAA,QAOQ,OAAA;EAAA,QANZ,OAAA;EAAA,QACA,YAAA;EAAA,QACA,aAAA;EAAA,QACA,cAAA;EAAA,QACA,WAAA;cAEY,OAAA,EAAS,oBAAA;EvCsQqC;;;;EuCzPlE,gBAAA,CAAiB,QAAA,EAAU,aAAA;EvC6RxB;;;;EuC9QH,QAAA,kBAA0B,mBAAA,CAAA,CACzB,UAAA,EAAY,gBAAA,CAAiB,QAAA,IAC3B,cAAA,CAAe,QAAA;EvC8WR;;;EuCzVV,WAAA,CAAY,WAAA,EAAa,gBAAA;EvCsbqB;;;;EuC5a9C,UAAA,CAAW,QAAA;EvCqhBsC;;;EuCjgB3C,OAAA,CAAQ,QAAA,WAAmB,OAAA,CAAQ,UAAA;EvCmhB9B;;;EuCvfL,QAAA,CAAS,QAAA,WAAmB,OAAA,CAAQ,UAAA;EvCkoBmB;;;EuCvlBvD,UAAA,CAAW,QAAA,WAAmB,OAAA,CAAQ,UAAA;EvCmqBmB;;;EuCnoBzD,SAAA,CAAU,QAAA,UAAkB,UAAA,aAA8B,OAAA,CAAQ,UAAA;EvC4tBhB;;;EuCxrBlD,oBAAA,CACL,OAAA,EAAS,MAAA,mBACT,UAAA,UACA,KAAA,YACE,OAAA;IACF,OAAA,EAAS,MAAA;IACT,OAAA,EAAS,UAAA,CAAW,MAAA;EAAA;EvCy8BiD;;;EuCh8BhE,mBAAA,CACL,OAAA,EAAS,MAAA,mBACT,UAAA,UACA,KAAA,YACE,OAAA,CAAQ,UAAA;EvC8hCgC;;;EuCthCrC,sBAAA,CACL,EAAA,UACA,UAAA,WACE,OAAA;IAAU,OAAA;IAAkB,OAAA,EAAS,UAAA;EAAA;EvC9MlC;;;EuCsNA,qBAAA,CACL,EAAA,UACA,UAAA,UACA,SAAA,YACE,OAAA,CAAQ,UAAA;EvC1NsC;;;EuCkO3C,sBAAA,CACL,OAAA,EAAS,MAAA,mBACT,UAAA,WACE,OAAA,CAAQ,UAAA;EvClI0C;;;EuC0I/C,wBAAA,CACL,OAAA,EAAS,MAAA,mBACT,UAAA,WACE,OAAA,CAAQ,UAAA;EvCrFmB;;;EuC6FxB,oBAAA,CAAqB,IAAA;IAAQ,IAAA;IAAc,IAAA;IAAc,IAAA;EAAA,IAAiB,OAAA;IAC/E,IAAA;MAAQ,IAAA;MAAc,IAAA;MAAc,IAAA;IAAA;IACpC,OAAA,EAAS,UAAA;MAAa,IAAA;MAAc,IAAA;MAAc,IAAA;IAAA;EAAA;EvCRxC;;;EuCiBL,mBAAA,CAAoB,KAAA,EAAO,WAAA,GAAY,OAAA,CAAQ,UAAA;EvCNpD;;;;EuCeK,cAAA,CAAe,QAAA,UAAkB,KAAA,EAAO,SAAA,GAAY,OAAA;EvCqBzC;;;EuCNX,WAAA,CACL,QAAA,UACA,SAAA,UACA,OAAA,EAAS,kBAAA,GACP,OAAA,CAAQ,WAAA;EvCE4D;;;EuCMvE,eAAA,CAAgB,QAAA;EvC2Bf;;;EuCfD,SAAA,CAAU,QAAA,WAAmB,cAAA;EvC4C5B;;;EuCrCD,cAAA,CAAe,QAAA,WAAmB,WAAA;EvCuCvB;;;EuChCX,aAAA,CAAA,GAAiB,KAAA;IAAQ,MAAA,EAAQ,cAAA;IAAgB,KAAA,EAAO,WAAA;EAAA;EvC4HW;;;EuClHnE,gBAAA,CAAA,GAAoB,cAAA;EvCgLO;;;EuCvK3B,SAAA,CAAU,QAAA;EvCwLkB;;;EuCjL5B,QAAA,CAAS,QAAA;EvC2M2B;;;EuChMpC,yBAAA,CAA0B,QAAA,WAAmB,KAAA;IAAQ,QAAA;IAAkB,UAAA;EAAA;EvCkNpE;;;EuCpMG,yBAAA,CAA0B,QAAA,WAAmB,OAAA;EvC4Q7C;;;;EuCnQA,yBAAA,CAA0B,QAAA,UAAkB,QAAA,kBAA0B,OAAA;EvCsUtE;;;;;;EuCzSA,qBAAA,CAAsB,cAAA,GAAiB,GAAA,qBAAwB,OAAA;EvC0U1B;;;;EuCpTrC,qBAAA,CAAA,GAAyB,OAAA,CAC9B,KAAA;IACC,QAAA;IACA,SAAA,EAAW,KAAA;MAAQ,QAAA;IAAA;IACnB,gBAAA;EAAA;EvC2V6D;;;EAAA,QuChUvD,iBAAA;EvC8WuB;;;EuCvV/B,YAAA,CAAA;EvCkYyB;;;;EAAA,QuCzXX,eAAA;AAAA;;;;iBAeC,mBAAA,CAAoB,OAAA,EAAS,oBAAA,GAAuB,aAAA;;;;;;;KCrmBxD,gBAAA,IAAoB,QAAA,UAAkB,KAAA,EAAO,SAAA,KAAc,OAAA;;;;;;;;cAgB1D,YAAA;EAAA,QAEH,EAAA;EAAA,QACA,cAAA;cADA,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,cAAA,EAAgB,gBAAA;;AzClC1B;;;;;;;EyC6CO,IAAA,CAAA,GAAQ,OAAA;ExCmEF;;;;EwCwDN,iBAAA,CAAA,GAAqB,OAAA;ExClDP;;;;EwCmEd,cAAA,CAAA,GAAkB,OAAA;AAAA;;;;;;;;;;;;;UCvLR,aAAA;;EAEhB,KAAA,WAAgB,OAAA;;EAEhB,IAAA,WAAe,OAAA;;EAEf,UAAA;;EAEA,gBAAA,CAAiB,EAAA,EAAI,eAAA;AAAA;;;;;KAOV,eAAA,SAAwB,OAAA;;;cCCvB,iBAAA,YAA6B,aAAA;EAAA,QAKrB,QAAA;EAAA,QAJZ,KAAA;EAAA,QACA,OAAA;EAAA,QACA,aAAA;cAEY,QAAA,EAAU,YAAA;EAE9B,gBAAA,CAAiB,EAAA,EAAI,eAAA;EAIrB,KAAA,CAAA;EAKA,IAAA,CAAA;EAQA,UAAA,CAAA;EAAA,QAUQ,GAAA;EAAA,QA2CA,WAAA;AAAA;;;;;;cC1FI,wBAAA,SAAiC,KAAA;EAAA,WAAA,CAAA;AAAA;;;;A7CT9C;;;;;;c6C8Ba,iBAAA,YAA6B,aAAA;E7C5BxC;;;E6CgCD,WAAA,CAAA;;;;EAOA,SAAA,CAAA;E5CrC8B;;;E4C4CxB,IAAA,CAEL,SAAA,EAAW,cAAA,EAEX,KAAA,WACE,OAAA,CAAQ,uBAAA;;;;EAOX,YAAA,CAAA;E3CwD6B;;;E2CjDvB,YAAA,CAAA,GAAgB,OAAA;AAAA;;;;;iBASP,uBAAA,CAAwB,QAAA,GAAW,cAAA,GAAiB,aAAA;;;;;;;;;;;;;;UChEnD,mBAAA;;EAEhB,GAAA;;EAEA,GAAA;;EAEA,GAAA;AAAA;;;;UAMgB,2BAAA;E9CrBC;E8CuBjB,SAAA;E9CxBC;E8C0BD,SAAA;E9CzBgC;E8C2BhC,MAAA;AAAA;;;A7C1BD;;;;;;;;ACgHA;;iB4CGsB,oBAAA,CAAqB,OAAA,EAAS,2BAAA,GAA8B,OAAA;;;;KAkCtE,wBAAA;EACP,KAAA;EAAa,OAAA,EAAS,mBAAA;AAAA;EACtB,KAAA;EAAc,KAAA;AAAA;;;;KAKP,yBAAA;E5CwNA,sC4CtNX,MAAA;AAAA;EAE8C,GAAA,EAAK,GAAA;AAAA;E5CyRhD,yC4CvRyC,KAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;iBA0BvB,kBAAA,CACrB,OAAA,EAAS,yBAAA,GACP,OAAA,CAAQ,wBAAA;;;;iBAmEK,cAAA,CAAe,SAAA;EAC9B,UAAA;EACA,EAAA;AAAA;;;;;;;;;;;UC/PgB,oBAAA;;EAEhB,UAAA;;EAEA,EAAA;;EAEA,MAAA;;EAEA,SAAA;;EAEA,OAAA;E/ClBgC;;;;E+CuBhC,WAAA;;;;;;;EAOA,MAAA;AAAA;A9C1BD;;;;;;;;ACgHA;;;;;;;;;;;;;;;;;;;;;;;;;ADhHA,iB8C8DsB,aAAA,CAAc,OAAA,EAAS,oBAAA,GAAuB,OAAA;;;;;;;;;;;;iBAmDpD,eAAA,CAAgB,OAAA;EAC/B,IAAA;EACA,KAAA;EACA,OAAA;AAAA;;;;;;;;;;;;;;iBCjHe,gBAAA,CAAiB,GAAA,EAAK,GAAA;;;;;;;AhDPtC;;iBgDmBgB,eAAA,CAAgB,GAAA,EAAK,GAAA;;;AhDnBrC;;;;;;;;;;;;;;ACIA;;;;ADJA,iBiD2HsB,cAAA,WAAyB,cAAA,CAAA,CAC9C,GAAA,EAAK,CAAA,GACH,OAAA,CAAQ,YAAA,CAAa,CAAA;;;;;;;;;;;;;;;;iBA+ER,eAAA,CAAA,GAAmB,OAAA,CAAQ,OAAA,CAAQ,YAAA;;;;;;;;;;;;;;;;;;;;;;;iBA8E7B,eAAA,CACrB,QAAA,EAAU,OAAA,CAAQ,YAAA,GAClB,EAAA,EAAI,MAAA,CAAO,QAAA,IACT,OAAA;;;;;;;iBAwBmB,gBAAA,aAAA,CACrB,QAAA,UACA,GAAA,WACE,OAAA,CAAQ,CAAA;;;;;;;iBA0BW,iBAAA,CAAkB,QAAA,WAAmB,OAAA,CAAQ,MAAA;;;UCzUlD,kBAAA;EAChB,UAAA;EACA,SAAA;EACA,QAAA;AAAA;AAAA,UAGgB,iBAAA;EAChB,KAAA,EAAO,aAAA;EACP,KAAA;AAAA;AlDjBD;;;;;;;;;;;;;;AAAA,iBkDkCsB,WAAA,CAAY,OAAA,EAAS,kBAAA,GAAqB,OAAA,CAAQ,iBAAA;AhDkFxE;;;;;;;;;;AAAA,iBgDlCsB,eAAA,CAAgB,UAAA,UAAoB,SAAA,WAAoB,OAAA;;;;;;KCpFlE,YAAA;;;;UAKK,QAAA;EAChB,EAAA;EACA,KAAA;EACA,GAAA;EACA,MAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA,EAAU,QAAA;AAAA;;;AnDVX;UmDgBiB,IAAA;EAChB,EAAA;EACA,IAAA;EACA,KAAA;EACA,KAAA,EAAO,QAAA;EACP,MAAA;EACA,gBAAA;AAAA;;;;UAwCgB,mBAAA;EAChB,IAAA,EAAM,YAAA;EACN,KAAA;EjD2ZW;;;;;EiDrZX,mBAAA;EACA,WAAA;EACA,SAAA;EACA,MAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,SAAA;AAAA;;;;UAMgB,mBAAA;EAChB,KAAA;EACA,SAAA;EACA,MAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,SAAA;AAAA;;;;UAMgB,eAAA;EAChB,IAAA;EACA,KAAA;EACA,MAAA;EjDwvCoC;EiDtvCpC,aAAA;AAAA;;;;UAMgB,eAAA;EAChB,KAAA;AAAA;;;;UAMgB,qBAAA;EAChB,KAAA,EAAO,KAAA;IACN,EAAA;IACA,QAAA;IACA,SAAA;EAAA;AAAA;;;UCtGe,gBAAA;;;EAGhB,MAAA;AAAA;;ApDnBD;;;;;;;;iBoD+BgB,OAAA,CAAQ,IAAA,UAAc,OAAA,GAAS,gBAAA,GAAwB,OAAA,CAAQ,IAAA;AnD3B/E;;;;AAAA,iBmDqFsB,QAAA,CACrB,OAAA,GAAS,gBAAA,GACP,OAAA,CAAQ,KAAA;EAAQ,EAAA;EAAY,IAAA;EAAc,KAAA;EAAe,MAAA;AAAA;;;;;;;AnDvF5D;;;;;;;;ACgHA;iBmDnEsB,SAAA,CAAU,EAAA,WAAa,OAAA,CAAQ,aAAA;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+B/B,eAAA,CACrB,IAAA,UACA,OAAA;EAAY,MAAA;AAAA,IACV,OAAA,CAAQ,aAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2QW,kBAAA,CACrB,UAAA,UACA,MAAA,qBACA,OAAA;EACC,MAAA;EACA,OAAA,GAAU,MAAA;EACV,MAAA;EACA,KAAA;AAAA,IAEC,OAAA,CAAQ,KAAA;EAAQ,EAAA;EAAY,IAAA,EAAM,MAAA;AAAA;;;;;;;;;UCrWpB,WAAA;EAChB,EAAA;EACA,IAAA;EACA,KAAA;EACA,aAAA;EACA,YAAA;EACA,WAAA;EACA,MAAA;EACA,gBAAA;AAAA;;;AtDVD;UsDgBiB,YAAA;EAChB,EAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,QAAA;EACA,WAAA;EACA,QAAA,EAAU,YAAA;EACV,KAAA;EACA,MAAA;EACA,gBAAA;AAAA;;ArDtBD;;UqD4BiB,eAAA;EAChB,EAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,SAAA;EACA,IAAA;EACA,MAAA;EACA,iBAAA;AAAA;;;;UAMgB,eAAA;EAChB,IAAA;EACA,KAAA;EACA,QAAA;EACA,WAAA;EACA,MAAA;EpDiSkE;;EoD9RlE,aAAA;AAAA;;;;UAMgB,eAAA;EAChB,IAAA;EACA,KAAA;EACA,QAAA;EACA,WAAA;AAAA;;;UCjDgB,oBAAA;EAChB,MAAA;AAAA;;;AvDhBD;iBuDsBgB,mBAAA,CAAA;;;;;;iBASM,eAAA,CAAgB,OAAA,GAAS,oBAAA,GAA4B,OAAA,CAAQ,WAAA;;;;;;;AtD3BnF;;;iBsD+CsB,cAAA,CACrB,IAAA,UACA,OAAA,GAAS,oBAAA,GACP,OAAA,CAAQ,WAAA;;;;;iBA4CW,gBAAA,CACrB,YAAA,UACA,OAAA,GAAS,oBAAA,GACP,OAAA,CAAQ,YAAA;;;;;;iBA0EW,OAAA,CACrB,YAAA,UACA,IAAA,UACA,OAAA,GAAS,oBAAA,GACP,OAAA,CAAQ,YAAA;;;;;iBAyEK,aAAA,CACf,UAAA,UACA,OAAA,UACA,YAAA,WACA,OAAA,GAAS,oBAAA,GACP,OAAA,CAAQ,YAAA;;;;iBAmCW,kBAAA,CACrB,UAAA,UACA,QAAA,YACA,YAAA,UACA,OAAA,GAAS,oBAAA,GACP,OAAA,CAAQ,GAAA,SAAY,YAAA;;;;;iBA4GD,qBAAA,CACrB,UAAA,UACA,QAAA,YACA,OAAA,GAAS,oBAAA,GACP,OAAA,CAAQ,GAAA,SAAY,MAAA,SAAe,YAAA;;;;;iBA2HhB,gBAAA,CACrB,UAAA,UACA,YAAA,UACA,QAAA,UACA,OAAA,GAAS,oBAAA,GACP,OAAA,CAAQ,KAAA;EAAQ,EAAA;EAAY,IAAA,EAAM,MAAA;AAAA;;;KC5hBzB,UAAA;AAAA,UAEK,MAAA;EAChB,EAAA;EACA,IAAA,EAAM,UAAA;EACN,KAAA;EAEA,OAAA,GAAU,mBAAA;EACV,QAAA;EACA,WAAA;EACA,cAAA,GAAiB,MAAA;AAAA;AAAA,UAGD,UAAA;EAChB,EAAA;EACA,IAAA;EACA,KAAA;EACA,WAAA;EACA,OAAA,EAAS,MAAA;AAAA;AAAA,UA0BO,kBAAA;EAChB,EAAA;EACA,KAAA;EACA,WAAA;EACA,KAAA,EAAO,MAAA,SAAe,OAAA;AAAA;AAAA,UAGN,OAAA;EAChB,IAAA;EACA,KAAA;EACA,OAAA;EACA,OAAA,GAAU,KAAA;IAAQ,KAAA;IAAe,KAAA;EAAA;AAAA;AAAA,UAIjB,qBAAA;EAChB,IAAA;EACA,KAAA;EACA,WAAA;AAAA;AAAA,UAGgB,iBAAA;EAChB,IAAA,EAAM,UAAA;EACN,KAAA;EACA,OAAA,GAAU,mBAAA;EACV,QAAA;EACA,WAAA;EACA,cAAA,GAAiB,MAAA;AAAA;AAAA,UAGD,iBAAA,SAA0B,OAAA,CAAQ,iBAAA;AAAA,UAElC,mBAAA;EAChB,SAAA;AAAA;;;;;;;;;;iBCvDqB,aAAA,CAAc,IAAA,WAAe,OAAA,CAAQ,UAAA;;;;iBAgErC,cAAA,CAAA,GAAkB,OAAA,CAAQ,UAAA;;;;iBAmChC,mBAAA,CAAA,GAAuB,kBAAA;;;;;;;;;;;UClHtB,YAAA;;EAEhB,OAAA;;EAEA,OAAA,GAAU,MAAA;AAAA;;;;UAMM,aAAA;E1DdJ;E0DgBZ,WAAA;;EAEA,MAAA;E1DlBwC;E0DoBxC,MAAA;;EAEA,KAAA;E1DnBiB;E0DqBjB,MAAA;AAAA;;;;UAMgB,uBAAA;EzD1Bc;EyD4B9B,MAAA;EzD3BA;EyD6BA,MAAA;;EAEA,KAAA;;EAEA,MAAA;AAAA;;;;UAMgB,YAAA;ExD8EiC;EwD5EjD,UAAA;ExD+KuE;EwD7KvE,EAAA;ExDqO8D;EwDnO9D,IAAA;ExD6Q0C;EwD3Q1C,MAAA;ExDgS0D;EwD9R1D,KAAA;ExDsTG;;;;;;;;EwD7SH,OAAA;ExD4ZW;EwD1ZX,KAAA;AAAA;;;;UAMgB,cAAA;ExDylBiC;EwDvlBjD,KAAA,EAAO,YAAA;ExDwmBG;EwDtmBV,UAAA;AAAA;;;;UAMgB,cAAA;ExD6wB2B;EwD3wB3C,WAAA;ExDszBuE;EwDpzBvE,MAAA;ExDk2BoD;EwDh2BpD,KAAA;AAAA;;;;UAMgB,UAAA;ExDu8Bb;EwDr8BH,UAAA;ExDimC2C;EwD/lC3C,EAAA;ExD0rCsD;EwDxrCtD,KAAA;AAAA;;;;UAMgB,WAAA;EAChB,WAAA,EAAa,MAAA;IxDWkB,gCwDP7B,OAAA,UxDYI;IwDVJ,WAAA;EAAA;AAAA;;;;;;;;;cCjGU,UAAA;EAAA,QACQ,EAAA;cAAA,EAAA,EAAI,MAAA,CAAO,QAAA;;A3DhBhC;;;U2DsBS,cAAA;E3DtBgC;;;;E2DmCxC,eAAA,CAAgB,cAAA;E3DhCgB;;;E2DwChC,mBAAA,CAAoB,cAAA;;A1DvCrB;;E0D+CO,cAAA,CAAe,cAAA,WAAyB,OAAA;E1D9C9C;;;;;AC+GD;;;EyDpDO,cAAA,CACL,cAAA,UACA,gBAAA,YACA,QAAA,GAAW,MAAA,mBACT,OAAA;EzDiDqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAA,QyDkBV,cAAA;EzDsrBoB;;;EAAA,QyD1mBpB,YAAA;EzDsrByD;;;EyD1qBjE,YAAA,CAAa,cAAA,WAAyB,OAAA;EzDmwBoB;;;;;EyDlvB1D,YAAA,CACL,cAAA,UACA,gBAAA,YACA,OAAA,GAAU,MAAA,mBACR,OAAA;EzD48BgD;;;EyD77B7C,mBAAA,CAAoB,cAAA,UAAwB,gBAAA,aAA6B,OAAA;EzDwhCjC;;;EyDpgCxC,eAAA,CAAgB,cAAA,WAAyB,OAAA,CAAQ,YAAA;EzDlKnC;;;EyD2Md,eAAA,CAAgB,cAAA,UAAwB,MAAA,EAAQ,YAAA,GAAe,OAAA;EzD3MjD;;;EyDsNd,mBAAA,CAAoB,cAAA,WAAyB,OAAA;EzDjNV;;;;;;;;EyD8OnC,YAAA,CACL,cAAA,UACA,OAAA;IAAY,OAAA,GAAU,MAAA;EAAA,IACpB,OAAA;EzDtF2D;;;;;EyDmHxD,aAAA,CAAc,cAAA,WAAyB,OAAA;EzDzEK;;;EyDmF5C,aAAA,CACL,cAAA,WACE,OAAA;IAAU,OAAA;IAAiB,WAAA;EAAA;EzD3C7B;;;;;;;;;;;;;EyD6EK,oBAAA,CAAqB,cAAA,WAAyB,OAAA;EzD3BrB;;;;;;EyDiFzB,kBAAA,CAAA,GAAsB,OAAA;AAAA;;;;;;;;;;;;;A3D5e7B;;;;;;;;;iB4D2DsB,MAAA,CAAO,KAAA,UAAe,OAAA,GAAS,aAAA,GAAqB,OAAA,CAAQ,cAAA;;;;;A3DvDlF;;;;;;;iB2DuEsB,YAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,KAAA,UACA,OAAA,GAAS,aAAA,GACP,OAAA,CAAQ,cAAA;A1DqCX;;;;;;;;;;;;;;;;AAAA,iB0D2BsB,gBAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,UAAA,UACA,KAAA,UACA,OAAA,GAAS,uBAAA,GACP,OAAA,CAAQ,cAAA;;;;;;;;;iBAoKW,cAAA,CACrB,EAAA,EAAI,MAAA,CAAO,QAAA,GACX,KAAA,UACA,OAAA,GAAS,cAAA,GACP,OAAA,CAAQ,UAAA;;;;iBAiFW,cAAA,CAAe,EAAA,EAAI,MAAA,CAAO,QAAA,IAAY,OAAA,CAAQ,WAAA;;;;;;;;;;;;;;A5D7YpE;;;;;;;;;;iB6DiEgB,gBAAA,CAAiB,MAAA,EAAQ,iBAAA;;;;A5D7DzC;;;;;;iB4DiHgB,uBAAA,CACf,KAAA,EAAO,MAAA,mBACP,MAAA,aACE,MAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"init-lock-DlBHjf9-.mjs","names":[],"sources":["../src/after.ts","../src/utils/init-lock.ts"],"sourcesContent":["/**\n * Defer work past the HTTP response.\n *\n * Use for bookkeeping that doesn't need to complete before the client\n * gets bytes — writes that record state, maintenance queries, cache\n * refreshes. `after()` hands the promise to the host's lifetime\n * extender when one is available (Cloudflare's `waitUntil` under\n * workerd), or fires-and-forgets on Node (the process lives for the\n * next request anyway).\n *\n * Host binding is resolved lazily via a dynamic import of the\n * `virtual:emdash/wait-until` virtual module. Lazy — rather than a\n * static top-level import — so tools that walk the dist in a plain\n * Node loader (`astro check`, Vitest, etc.) don't trip over the\n * `virtual:` scheme: they'd only fail if they actually called\n * `after()`, which they don't during type-checking.\n */\n\nexport type WaitUntilFn = (promise: Promise<unknown>) => void;\n\n// Resolves to the host's waitUntil if the adapter provided one, or\n// null otherwise. Kicked off once at module load; subsequent `after()`\n// calls see the cached result without re-importing.\nconst waitUntilReady: Promise<WaitUntilFn | null> = (async () => {\n\ttry {\n\t\t// @ts-ignore - virtual module, generated by the Astro integration\n\t\tconst mod = (await import(\"virtual:emdash/wait-until\")) as {\n\t\t\twaitUntil?: WaitUntilFn;\n\t\t};\n\t\treturn mod.waitUntil ?? null;\n\t} catch {\n\t\t// No virtual module available (Node-side tooling, tests without the\n\t\t// integration in scope). Fire-and-forget is the safe fallback.\n\t\treturn null;\n\t}\n})();\n// Surface rejections without making the module-load fail.\nwaitUntilReady.catch(() => {});\n\n/**\n * Schedule `fn` to run without blocking the response.\n *\n * Errors are caught and logged — a deferred task should never surface\n * as an unhandled rejection because the response is long gone. Callers\n * that care about errors should handle them inside `fn`.\n */\nexport function after(fn: () => void | Promise<void>): void {\n\tconst promise = Promise.resolve()\n\t\t.then(fn)\n\t\t.catch((error) => {\n\t\t\tconsole.error(\"[emdash] deferred task failed:\", error);\n\t\t});\n\n\t// Defer the lifetime-extender handoff to the microtask that resolves\n\t// waitUntilReady. On workerd this is effectively instant (the virtual\n\t// module is already loaded in the bundle); on Node the promise\n\t// resolves to null, so this is just one extra microtask and no-op.\n\tvoid waitUntilReady.then((waitUntil) => {\n\t\tif (waitUntil) waitUntil(promise);\n\t\treturn null;\n\t});\n}\n","/**\n * Reclaimable initialization lock for isolate-lifetime singletons.\n *\n * Guards \"first request initializes, everyone else waits\" sections\n * (runtime creation, database init) against a workerd failure mode: if the\n * request that owns the initialization is cancelled mid-await (client\n * disconnect, context teardown), its continuation — including any `finally`\n * that would release the lock — never runs. A plain boolean or shared\n * promise then stays stuck forever and every subsequent request in the\n * isolate hangs until the platform kills it (observed as 524s at the\n * 100-second wall limit, with the isolate poisoned until eviction).\n *\n * This lock instead records *when* the owner started. Waiters poll — we\n * deliberately never await a promise created by another request, which\n * workerd flags — and if the owner has held the lock past `deadlineMs`,\n * the next waiter assumes the owner is dead, reclaims the lock, and runs\n * the initialization itself. Waiters also give up after `maxWaitMs` so a\n * request degrades to an error response rather than hanging.\n */\n\nexport interface InitLock {\n\t/** Epoch ms when the current owner claimed the lock, or null when free. */\n\townerStartedAt: number | null;\n\t/**\n\t * Monotonic claim counter identifying the current owner. Release is\n\t * gated on it: a slow owner that finishes after a waiter has reclaimed\n\t * the lock must not clear the reclaimer's claim — that would let yet\n\t * another caller claim the lock and start a third concurrent init.\n\t */\n\tgeneration: number;\n}\n\nexport function createInitLock(): InitLock {\n\treturn { ownerStartedAt: null, generation: 0 };\n}\n\nexport interface InitLockOptions {\n\t/**\n\t * Reclaim the lock if the owner has held it longer than this. Must be\n\t * comfortably above the slowest legitimate init (cold migrations on a\n\t * contended D1, including the concurrent-migrator wait) — a too-short\n\t * deadline risks two concurrent inits, a too-long one delays recovery\n\t * of a poisoned isolate. Nested locks must compose: an outer lock's\n\t * deadline must exceed the deadline of any lock its init acquires.\n\t */\n\tdeadlineMs?: number;\n\t/** Waiter poll interval. */\n\tpollMs?: number;\n\t/**\n\t * Give up waiting after this long and throw instead of hanging.\n\t * Defaults to `deadlineMs` plus headroom so a waiter always survives\n\t * long enough to reclaim a dead owner before giving up.\n\t */\n\tmaxWaitMs?: number;\n\t/**\n\t * Called with the in-flight init promise (errors pre-swallowed) so the\n\t * caller can hand it to the host's lifetime extender (waitUntil via\n\t * `after()`). If the owning request is cancelled mid-init, the anchored\n\t * promise keeps the context alive: init completes, populates the cache,\n\t * and the `finally` below releases the lock — preventing the poisoning\n\t * instead of merely recovering from it via reclaim.\n\t */\n\tanchor?: (promise: Promise<void>) => void;\n}\n\nconst DEFAULT_DEADLINE_MS = 15_000;\nconst DEFAULT_POLL_MS = 50;\nconst MAX_WAIT_HEADROOM_MS = 15_000;\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Return the cached value if present, otherwise initialize it under the\n * lock. `init` is responsible for storing the value so that `getCached`\n * returns it on subsequent calls — waiters re-check `getCached` after the\n * owner finishes rather than sharing the owner's promise.\n *\n * `init` receives an `isCurrentClaim` predicate and must gate its cache\n * publication on it: a slow init that was reclaimed past the deadline\n * must not overwrite the value published by the reclaimer (for the\n * runtime singleton that would orphan the reclaimer's active cron\n * scheduler). A losing init should also tear down any side resources it\n * started, since its result will never be published.\n */\nexport async function initWithLock<T>(\n\tlock: InitLock,\n\tgetCached: () => T | null | undefined,\n\tinit: (isCurrentClaim: () => boolean) => Promise<T>,\n\toptions?: InitLockOptions,\n): Promise<T> {\n\tconst deadlineMs = options?.deadlineMs ?? DEFAULT_DEADLINE_MS;\n\tconst pollMs = options?.pollMs ?? DEFAULT_POLL_MS;\n\tconst maxWaitMs = options?.maxWaitMs ?? deadlineMs + MAX_WAIT_HEADROOM_MS;\n\t// Date.now() is deliberate and only works because every loop iteration\n\t// awaits: in workerd the clock only advances across I/O, so a sync spin\n\t// would never observe the deadline. Don't \"optimize\" away the sleep.\n\tconst waitStart = Date.now();\n\n\tfor (;;) {\n\t\tconst cached = getCached();\n\t\tif (cached !== null && cached !== undefined) {\n\t\t\treturn cached;\n\t\t}\n\n\t\tconst ownerStartedAt = lock.ownerStartedAt;\n\t\tif (ownerStartedAt === null || Date.now() - ownerStartedAt > deadlineMs) {\n\t\t\t// Free, or the owner has been gone past the deadline — claim it.\n\t\t\t// Synchronous between awaits, so two waiters can't both claim.\n\t\t\tlock.generation += 1;\n\t\t\tconst claim = lock.generation;\n\t\t\tlock.ownerStartedAt = Date.now();\n\t\t\ttry {\n\t\t\t\t// Promise.resolve().then(...) so a synchronous throw from\n\t\t\t\t// init still becomes a rejection after the anchor attaches.\n\t\t\t\tconst isCurrentClaim = () => lock.generation === claim;\n\t\t\t\tconst initPromise = Promise.resolve().then(() => init(isCurrentClaim));\n\t\t\t\toptions?.anchor?.(\n\t\t\t\t\tinitPromise.then(\n\t\t\t\t\t\t() => undefined,\n\t\t\t\t\t\t() => undefined,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\treturn await initPromise;\n\t\t\t} finally {\n\t\t\t\t// If this request dies mid-init unanchored this never runs;\n\t\t\t\t// the next waiter reclaims after deadlineMs instead. Release\n\t\t\t\t// only while still the current owner: a reclaimer may have\n\t\t\t\t// taken the lock while this (slow) init was running, and\n\t\t\t\t// clearing its claim would admit a third concurrent init.\n\t\t\t\tif (lock.generation === claim) {\n\t\t\t\t\tlock.ownerStartedAt = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (Date.now() - waitStart > maxWaitMs) {\n\t\t\tthrow new Error(`initWithLock: timed out after ${maxWaitMs}ms waiting for initialization`);\n\t\t}\n\t\tawait sleep(pollMs);\n\t}\n}\n"],"mappings":";AAuBA,MAAM,kBAA+C,YAAY;AAChE,KAAI;AAKH,UAHa,MAAM,OAAO,8BAGf,aAAa;SACjB;AAGP,SAAO;;IAEL;AAEJ,eAAe,YAAY,GAAG;;;;;;;;AAS9B,SAAgB,MAAM,IAAsC;CAC3D,MAAM,UAAU,QAAQ,SAAS,CAC/B,KAAK,GAAG,CACR,OAAO,UAAU;AACjB,UAAQ,MAAM,kCAAkC,MAAM;GACrD;AAMH,CAAK,eAAe,MAAM,cAAc;AACvC,MAAI,UAAW,WAAU,QAAQ;AACjC,SAAO;GACN;;;;;AC5BH,SAAgB,iBAA2B;AAC1C,QAAO;EAAE,gBAAgB;EAAM,YAAY;EAAG;;AAgC/C,MAAM,sBAAsB;AAC5B,MAAM,kBAAkB;AACxB,MAAM,uBAAuB;AAE7B,SAAS,MAAM,IAA2B;AACzC,QAAO,IAAI,SAAS,YAAY,WAAW,SAAS,GAAG,CAAC;;;;;;;;;;;;;;;AAgBzD,eAAsB,aACrB,MACA,WACA,MACA,SACa;CACb,MAAM,aAAa,SAAS,cAAc;CAC1C,MAAM,SAAS,SAAS,UAAU;CAClC,MAAM,YAAY,SAAS,aAAa,aAAa;CAIrD,MAAM,YAAY,KAAK,KAAK;AAE5B,UAAS;EACR,MAAM,SAAS,WAAW;AAC1B,MAAI,WAAW,QAAQ,WAAW,OACjC,QAAO;EAGR,MAAM,iBAAiB,KAAK;AAC5B,MAAI,mBAAmB,QAAQ,KAAK,KAAK,GAAG,iBAAiB,YAAY;AAGxE,QAAK,cAAc;GACnB,MAAM,QAAQ,KAAK;AACnB,QAAK,iBAAiB,KAAK,KAAK;AAChC,OAAI;IAGH,MAAM,uBAAuB,KAAK,eAAe;IACjD,MAAM,cAAc,QAAQ,SAAS,CAAC,WAAW,KAAK,eAAe,CAAC;AACtE,aAAS,SACR,YAAY,WACL,cACA,OACN,CACD;AACD,WAAO,MAAM;aACJ;AAMT,QAAI,KAAK,eAAe,MACvB,MAAK,iBAAiB;;;AAKzB,MAAI,KAAK,KAAK,GAAG,YAAY,UAC5B,OAAM,IAAI,MAAM,iCAAiC,UAAU,+BAA+B;AAE3F,QAAM,MAAM,OAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"loader-BqWjcH3h.mjs","names":[],"sources":["../src/loader.ts"],"sourcesContent":["/**\n * Astro Live Collections loader for EmDash\n *\n * This loader implements the Astro LiveLoader interface to fetch content\n * at runtime from the database, enabling live editing without rebuilds.\n *\n * Architecture:\n * - Single `_emdash` Astro collection handles all content types\n * - Dialect comes from virtual module (configured in astro.config.mjs)\n * - Each content type maps to its own database table: ec_posts, ec_products, etc.\n * - `getEmDashCollection()` / `getEmDashEntry()` wrap Astro's live collection API\n */\n\nimport type { LiveLoader } from \"astro/loaders\";\nimport { Kysely, sql, type Dialect } from \"kysely\";\n\nimport { currentTimestampValue, isPostgres } from \"./database/dialect-helpers.js\";\nimport { kyselyLogOption } from \"./database/instrumentation.js\";\nimport { decodeCursor, encodeCursor } from \"./database/repositories/types.js\";\nimport { validateIdentifier } from \"./database/validate.js\";\nimport type { Database } from \"./index.js\";\nimport { getRequestContext } from \"./request-context.js\";\nimport { isMissingColumnError, isMissingTableError } from \"./utils/db-errors.js\";\n\nconst FIELD_NAME_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\n/**\n * SEO columns joined in from `_emdash_seo` on the single-entry path, mapped to\n * aliased result keys. SEO lives in a side table, so a LEFT JOIN folds it into\n * the entry load at zero extra query cost; the result is surfaced as a nested\n * `data.seo` object (see extractSeo) rather than flat fields.\n *\n * The `_emdash_` prefix on the aliases guarantees they can never collide with\n * a content field. Field slugs must match `/^[a-z][a-z0-9_]*$/`, so a user can\n * legitimately define a `seo_title` field; selecting the joined column under\n * its bare name would shadow that field in the result set and drop the user's\n * value. The prefix (illegal as a leading slug char) sidesteps this entirely.\n */\nconst SEO_COLUMN_ALIASES: Record<string, string> = {\n\tseo_title: \"_emdash_seo_title\",\n\tseo_description: \"_emdash_seo_description\",\n\tseo_image: \"_emdash_seo_image\",\n\tseo_canonical: \"_emdash_seo_canonical\",\n\tseo_no_index: \"_emdash_seo_no_index\",\n};\n\n/** Aliased SEO result keys — excluded from generic field mapping. */\nconst SEO_ALIAS_COLUMNS = Object.values(SEO_COLUMN_ALIASES);\n\n/**\n * System columns excluded from entry.data\n * Note: slug is intentionally NOT excluded - it's useful as data.slug in templates\n */\nconst SYSTEM_COLUMNS = new Set([\n\t\"id\",\n\t// \"slug\" - kept in data for template access\n\t\"status\",\n\t\"author_id\",\n\t\"primary_byline_id\",\n\t\"created_at\",\n\t\"updated_at\",\n\t\"published_at\",\n\t\"scheduled_at\",\n\t\"deleted_at\",\n\t\"version\",\n\t\"live_revision_id\",\n\t\"draft_revision_id\",\n\t\"locale\",\n\t\"translation_group\",\n\t// Aliased SEO columns joined from _emdash_seo on the single-entry path.\n\t// Surfaced as a nested data.seo object (see extractSeo), never as flat\n\t// fields. The aliases are _emdash_-prefixed so they can't shadow a user\n\t// field named e.g. `seo_title`.\n\t...SEO_ALIAS_COLUMNS,\n]);\n\n/** Resolved SEO shape attached to `entry.data.seo`. Mirrors `ContentSeo`. */\ninterface EntrySeo {\n\ttitle: string | null;\n\tdescription: string | null;\n\timage: string | null;\n\tcanonical: string | null;\n\tnoIndex: boolean;\n}\n\n/**\n * Build a `data.seo` object from the joined `_emdash_seo` columns on a row.\n *\n * Returns `null` when no SEO row exists (LEFT JOIN miss → `seo_no_index` is\n * NULL, since the column is `NOT NULL DEFAULT 0` whenever a row is present).\n * Returning null keeps the `seo` key off entries that have none, so\n * `getSeoMeta()` falls back to its defaults exactly as before.\n */\nfunction extractSeo(row: Record<string, unknown>): EntrySeo | null {\n\tconst noIndex = row[SEO_COLUMN_ALIASES.seo_no_index];\n\tif (noIndex === null || noIndex === undefined) return null;\n\tconst title = row[SEO_COLUMN_ALIASES.seo_title];\n\tconst description = row[SEO_COLUMN_ALIASES.seo_description];\n\tconst image = row[SEO_COLUMN_ALIASES.seo_image];\n\tconst canonical = row[SEO_COLUMN_ALIASES.seo_canonical];\n\treturn {\n\t\ttitle: typeof title === \"string\" ? title : null,\n\t\tdescription: typeof description === \"string\" ? description : null,\n\t\timage: typeof image === \"string\" ? image : null,\n\t\tcanonical: typeof canonical === \"string\" ? canonical : null,\n\t\tnoIndex: noIndex === 1,\n\t};\n}\n\n/**\n * Get the table name for a collection type\n */\nfunction getTableName(type: string): string {\n\tvalidateIdentifier(type, \"collection type\");\n\treturn `ec_${type}`;\n}\n\n/**\n * Cache for taxonomy names (only used for the primary database).\n * Skipped when a per-request DB override is active (e.g. preview mode)\n * because the override DB may have different taxonomies.\n */\nlet taxonomyNames: Set<string> | null = null;\n\n/**\n * Get all taxonomy names (cached for the primary DB, bypassed only when\n * the per-request DB is an isolated instance — playground / DO preview).\n * Plain D1 Sessions routing shares schema with the singleton, so the\n * module-scoped cache stays valid.\n */\nasync function getTaxonomyNames(db: Kysely<Database>): Promise<Set<string>> {\n\tconst hasIsolatedDb = getRequestContext()?.dbIsIsolated === true;\n\n\tif (!hasIsolatedDb && taxonomyNames) {\n\t\treturn taxonomyNames;\n\t}\n\n\ttry {\n\t\tconst defs = await db.selectFrom(\"_emdash_taxonomy_defs\").select(\"name\").execute();\n\t\tconst names = new Set(defs.map((d) => d.name));\n\t\tif (!hasIsolatedDb) {\n\t\t\ttaxonomyNames = names;\n\t\t}\n\t\treturn names;\n\t} catch {\n\t\t// Table doesn't exist yet, return empty set\n\t\tconst empty = new Set<string>();\n\t\tif (!hasIsolatedDb) {\n\t\t\ttaxonomyNames = empty;\n\t\t}\n\t\treturn empty;\n\t}\n}\n\n/**\n * System columns to include in data (mapped to camelCase where needed)\n */\nconst INCLUDE_IN_DATA: Record<string, string> = {\n\tid: \"id\",\n\tstatus: \"status\",\n\tauthor_id: \"authorId\",\n\tprimary_byline_id: \"primaryBylineId\",\n\tcreated_at: \"createdAt\",\n\tupdated_at: \"updatedAt\",\n\tpublished_at: \"publishedAt\",\n\tscheduled_at: \"scheduledAt\",\n\tdraft_revision_id: \"draftRevisionId\",\n\tlive_revision_id: \"liveRevisionId\",\n\tlocale: \"locale\",\n\ttranslation_group: \"translationGroup\",\n};\n\n/** System date columns that should be converted to Date objects */\nconst DATE_COLUMNS = new Set([\"created_at\", \"updated_at\", \"published_at\", \"scheduled_at\"]);\n\n/**\n * Hidden, symbol-keyed property on each mapped data record carrying the raw\n * DB string for every date column. Lets cursor encoders downstream reproduce\n * the loader's exact `nextCursor` format without round-tripping through\n * `new Date()`, which loses precision for stored values that aren't already\n * ISO-with-milliseconds (e.g. `2026-01-01T00:00:00Z` becomes\n * `2026-01-01T00:00:00.000Z`).\n */\nexport const CURSOR_RAW_VALUES: unique symbol = Symbol(\"emdash:cursorRawValues\");\n\nconst LOCAL_MEDIA_FILE_PREFIX = \"/_emdash/api/media/file/\";\nconst URL_SCHEME_PATTERN = /^[a-zA-Z][a-zA-Z\\d+\\-.]*:/;\n\n/** Safely extract a string value from a record, returning fallback if not a string */\nfunction rowStr(row: Record<string, unknown>, key: string, fallback = \"\"): string {\n\tconst val = row[key];\n\treturn typeof val === \"string\" ? val : fallback;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isBareMediaKey(src: string): boolean {\n\treturn !src.startsWith(\"/\") && !URL_SCHEME_PATTERN.test(src);\n}\n\nfunction normalizeLocalMediaValue(value: unknown): unknown {\n\tif (Array.isArray(value)) {\n\t\treturn value.map(normalizeLocalMediaValue);\n\t}\n\n\tif (!isRecord(value)) {\n\t\treturn value;\n\t}\n\n\tconst normalized: Record<string, unknown> = {};\n\tfor (const [key, child] of Object.entries(value)) {\n\t\tnormalized[key] = normalizeLocalMediaValue(child);\n\t}\n\n\tif (\n\t\tnormalized.provider === \"local\" &&\n\t\ttypeof normalized.src === \"string\" &&\n\t\tnormalized.src.length > 0\n\t) {\n\t\tconst src = normalized.src;\n\t\tif (src.startsWith(LOCAL_MEDIA_FILE_PREFIX)) {\n\t\t\tconst id = src.slice(LOCAL_MEDIA_FILE_PREFIX.length);\n\t\t\tif (!normalized.id && id) {\n\t\t\t\tnormalized.id = id;\n\t\t\t}\n\t\t} else if (isBareMediaKey(src)) {\n\t\t\tif (!normalized.id) {\n\t\t\t\tnormalized.id = src;\n\t\t\t}\n\t\t\tnormalized.src = `${LOCAL_MEDIA_FILE_PREFIX}${src}`;\n\t\t}\n\t}\n\n\treturn normalized;\n}\n\n/**\n * Map a database row to entry data\n * Extracts content fields (non-system columns) and parses JSON where needed.\n * System columns needed for templates (id, status, dates) are included with camelCase names.\n */\nfunction mapRowToData(row: Record<string, unknown>): Record<string, unknown> {\n\tconst data: Record<string, unknown> = {};\n\tconst rawDateValues: Record<string, string> = {};\n\n\tfor (const [key, value] of Object.entries(row)) {\n\t\t// Include certain system columns (mapped to camelCase where needed)\n\t\tif (key in INCLUDE_IN_DATA) {\n\t\t\t// Convert date columns from ISO strings to Date objects\n\t\t\tif (DATE_COLUMNS.has(key)) {\n\t\t\t\tif (typeof value === \"string\") {\n\t\t\t\t\trawDateValues[key] = value;\n\t\t\t\t\tdata[INCLUDE_IN_DATA[key]] = new Date(value);\n\t\t\t\t} else {\n\t\t\t\t\tdata[INCLUDE_IN_DATA[key]] = null;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdata[INCLUDE_IN_DATA[key]] = value;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (SYSTEM_COLUMNS.has(key)) continue;\n\n\t\t// Try to parse JSON strings (for portableText, json fields, etc.)\n\t\tif (typeof value === \"string\") {\n\t\t\ttry {\n\t\t\t\t// Only parse if it looks like JSON (starts with { or [)\n\t\t\t\tif (value.startsWith(\"{\") || value.startsWith(\"[\")) {\n\t\t\t\t\tdata[key] = normalizeLocalMediaValue(JSON.parse(value));\n\t\t\t\t} else {\n\t\t\t\t\tdata[key] = value;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tdata[key] = value;\n\t\t\t}\n\t\t} else {\n\t\t\tdata[key] = value;\n\t\t}\n\t}\n\n\tObject.defineProperty(data, CURSOR_RAW_VALUES, {\n\t\tvalue: rawDateValues,\n\t\tenumerable: false,\n\t\tconfigurable: false,\n\t\twritable: false,\n\t});\n\n\treturn data;\n}\n\n/**\n * Map revision data (already-parsed JSON object) to entry data.\n * Strips _-prefixed metadata keys (e.g. _slug) used internally by revisions.\n */\nfunction mapRevisionData(data: Record<string, unknown>): Record<string, unknown> {\n\tconst result: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(data)) {\n\t\tif (key.startsWith(\"_\")) continue; // revision metadata\n\t\tresult[key] = normalizeLocalMediaValue(value);\n\t}\n\treturn result;\n}\n\n// Virtual module imports are lazy-loaded to avoid errors when importing\n// emdash outside of Astro/Vite context (e.g., in astro.config.mjs)\nlet virtualConfig:\n\t| {\n\t\t\tdatabase?: { config: unknown };\n\t\t\ti18n?: { defaultLocale: string; locales: string[]; prefixDefaultLocale?: boolean } | null;\n\t }\n\t| undefined;\nlet virtualCreateDialect: ((config: unknown) => Dialect) | undefined;\n\nasync function loadVirtualModules() {\n\tif (virtualConfig === undefined) {\n\t\t// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n\t\t// @ts-ignore - virtual module\n\t\tconst configModule = await import(\"virtual:emdash/config\");\n\t\tvirtualConfig = configModule.default;\n\t}\n\tif (virtualCreateDialect === undefined) {\n\t\t// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n\t\t// @ts-ignore - virtual module\n\t\tconst dialectModule = await import(\"virtual:emdash/dialect\");\n\t\tvirtualCreateDialect = dialectModule.createDialect;\n\t\t// dialectType is no longer needed here — dialect detection is\n\t\t// done via the db adapter instance in dialect-helpers.ts\n\t}\n}\n\n/**\n * Entry data type - generic object\n */\nexport type EntryData = Record<string, unknown>;\n\n/**\n * Sort direction\n */\nexport type SortDirection = \"asc\" | \"desc\";\n\n/**\n * Order by specification - field name to direction\n * @example { created_at: \"desc\" } - Sort by created_at descending\n * @example { title: \"asc\" } - Sort by title ascending\n */\nexport type OrderBySpec = Record<string, SortDirection>;\n\n/**\n * Build WHERE clause for status filtering.\n * When filtering for 'published' status, also include scheduled content\n * whose scheduled_at time has passed (treating it as effectively published).\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nfunction buildStatusCondition(\n\tdb: Kysely<any>,\n\tstatus: string,\n\ttablePrefix?: string,\n): ReturnType<typeof sql> {\n\tconst statusField = tablePrefix ? `${tablePrefix}.status` : \"status\";\n\tconst scheduledAtField = tablePrefix ? `${tablePrefix}.scheduled_at` : \"scheduled_at\";\n\n\tif (status === \"published\") {\n\t\t// Include both published content AND scheduled content past its publish time.\n\t\t// scheduled_at is stored as text (ISO 8601). On Postgres, we must cast it\n\t\t// to timestamptz for the comparison with CURRENT_TIMESTAMP to work.\n\t\tconst scheduledAtExpr = isPostgres(db)\n\t\t\t? sql`${sql.ref(scheduledAtField)}::timestamptz`\n\t\t\t: sql.ref(scheduledAtField);\n\t\tconst nowExpr = isPostgres(db)\n\t\t\t? currentTimestampValue(db)\n\t\t\t: sql`strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`;\n\t\treturn sql`(${sql.ref(statusField)} = 'published' OR (${sql.ref(statusField)} = 'scheduled' AND ${scheduledAtExpr} <= ${nowExpr}))`;\n\t}\n\n\treturn sql`${sql.ref(statusField)} = ${status}`;\n}\n\n/**\n * Resolved primary sort field and direction (used for cursor pagination).\n */\ninterface PrimarySort {\n\tfield: string;\n\tdirection: SortDirection;\n}\n\n/**\n * Get the primary sort field from an orderBy spec (first valid field, or default).\n */\nfunction getPrimarySort(orderBy: OrderBySpec | undefined, tablePrefix?: string): PrimarySort {\n\tif (orderBy) {\n\t\tfor (const [field, direction] of Object.entries(orderBy)) {\n\t\t\tif (FIELD_NAME_PATTERN.test(field)) {\n\t\t\t\tconst fullField = tablePrefix ? `${tablePrefix}.${field}` : field;\n\t\t\t\treturn { field: fullField, direction };\n\t\t\t}\n\t\t}\n\t}\n\tconst defaultField = tablePrefix ? `${tablePrefix}.created_at` : \"created_at\";\n\treturn { field: defaultField, direction: \"desc\" };\n}\n\n/**\n * Build ORDER BY clause from orderBy spec\n * Validates field names to prevent SQL injection (alphanumeric + underscore only)\n * Supports multiple sort fields in object key order\n */\nfunction buildOrderByClause(\n\torderBy: OrderBySpec | undefined,\n\ttablePrefix?: string,\n): ReturnType<typeof sql> {\n\t// Default to created_at DESC\n\tif (!orderBy || Object.keys(orderBy).length === 0) {\n\t\tconst field = tablePrefix ? `${tablePrefix}.created_at` : \"created_at\";\n\t\treturn sql`ORDER BY ${sql.ref(field)} DESC, ${sql.ref(tablePrefix ? `${tablePrefix}.id` : \"id\")} DESC`;\n\t}\n\n\tconst sortParts: ReturnType<typeof sql>[] = [];\n\n\tfor (const [field, direction] of Object.entries(orderBy)) {\n\t\t// Validate field name (alphanumeric + underscore only)\n\t\tif (!FIELD_NAME_PATTERN.test(field)) {\n\t\t\tcontinue; // Skip invalid field names\n\t\t}\n\n\t\tconst fullField = tablePrefix ? `${tablePrefix}.${field}` : field;\n\t\tconst dir = direction === \"asc\" ? sql`ASC` : sql`DESC`;\n\t\tsortParts.push(sql`${sql.ref(fullField)} ${dir}`);\n\t}\n\n\t// If no valid sort fields, fall back to default\n\tif (sortParts.length === 0) {\n\t\tconst defaultField = tablePrefix ? `${tablePrefix}.created_at` : \"created_at\";\n\t\treturn sql`ORDER BY ${sql.ref(defaultField)} DESC, ${sql.ref(tablePrefix ? `${tablePrefix}.id` : \"id\")} DESC`;\n\t}\n\n\t// Add id as tiebreaker to ensure stable cursor ordering\n\tconst primary = getPrimarySort(orderBy, tablePrefix);\n\tconst idField = tablePrefix ? `${tablePrefix}.id` : \"id\";\n\tconst idDir = primary.direction === \"asc\" ? sql`ASC` : sql`DESC`;\n\tsortParts.push(sql`${sql.ref(idField)} ${idDir}`);\n\n\treturn sql`ORDER BY ${sql.join(sortParts, sql`, `)}`;\n}\n\n/**\n * Build a cursor WHERE condition for keyset pagination.\n * Uses the primary sort field + id as tiebreaker for stable ordering.\n *\n * Throws `InvalidCursorError` if the cursor is malformed; callers should\n * let this propagate so users see a real error rather than silently\n * falling back to the first page.\n */\nfunction buildCursorCondition(\n\tcursor: string,\n\torderBy: OrderBySpec | undefined,\n\ttablePrefix?: string,\n): ReturnType<typeof sql> {\n\tconst { orderValue, id: cursorId } = decodeCursor(cursor);\n\tconst primary = getPrimarySort(orderBy, tablePrefix);\n\tconst idField = tablePrefix ? `${tablePrefix}.id` : \"id\";\n\n\tif (primary.direction === \"desc\") {\n\t\treturn sql`(${sql.ref(primary.field)} < ${orderValue} OR (${sql.ref(primary.field)} = ${orderValue} AND ${sql.ref(idField)} < ${cursorId}))`;\n\t}\n\treturn sql`(${sql.ref(primary.field)} > ${orderValue} OR (${sql.ref(primary.field)} = ${orderValue} AND ${sql.ref(idField)} > ${cursorId}))`;\n}\n\n/** Type guard: is the where value a range object (not a string or array)? */\nfunction isWhereRange(value: WhereValue): value is WhereRange {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\n/**\n * Build AND conditions for non-taxonomy field filters.\n * Returns an array of sql fragments; empty if no field filters apply.\n * Field names are validated against FIELD_NAME_PATTERN to prevent injection.\n */\nfunction buildFieldConditions(\n\tfields: Record<string, WhereValue>,\n\ttablePrefix?: string,\n): ReturnType<typeof sql>[] {\n\tconst conditions: ReturnType<typeof sql>[] = [];\n\n\tfor (const [key, value] of Object.entries(fields)) {\n\t\tif (!FIELD_NAME_PATTERN.test(key)) {\n\t\t\tconsole.warn(`[emdash] where filter: invalid field name \"${key}\" ignored`);\n\t\t\tcontinue;\n\t\t}\n\t\tif (value == null) continue;\n\t\tconst ref = tablePrefix ? sql.ref(`${tablePrefix}.${key}`) : sql.ref(key);\n\n\t\tif (isWhereRange(value)) {\n\t\t\tif (value.gt !== undefined) conditions.push(sql`${ref} > ${value.gt}`);\n\t\t\tif (value.gte !== undefined) conditions.push(sql`${ref} >= ${value.gte}`);\n\t\t\tif (value.lt !== undefined) conditions.push(sql`${ref} < ${value.lt}`);\n\t\t\tif (value.lte !== undefined) conditions.push(sql`${ref} <= ${value.lte}`);\n\t\t} else if (Array.isArray(value)) {\n\t\t\tif (value.length > 0) {\n\t\t\t\tconditions.push(sql`${ref} IN (${sql.join(value.map((v) => sql`${v}`))})`);\n\t\t\t}\n\t\t} else {\n\t\t\tconditions.push(sql`${ref} = ${value}`);\n\t\t}\n\t}\n\n\treturn conditions;\n}\n\n/**\n * Range filter for comparison operators on field values.\n * Values are compared as strings in the database. This works correctly for\n * ISO 8601 dates (e.g. \"2024-01-01T00:00:00Z\") because lexicographic ordering\n * matches chronological ordering. Ensure date values use a consistent format.\n */\nexport interface WhereRange {\n\tgt?: string;\n\tgte?: string;\n\tlt?: string;\n\tlte?: string;\n}\n\n/**\n * A where clause value: exact match, multi-value match, or range comparison.\n */\nexport type WhereValue = string | string[] | WhereRange;\n\n/**\n * Filter for loadCollection - type is required\n */\nexport interface CollectionFilter {\n\ttype: string;\n\tstatus?: \"draft\" | \"published\" | \"archived\";\n\tlimit?: number;\n\t/**\n\t * Opaque cursor for keyset pagination.\n\t * Pass the `nextCursor` value from a previous result to fetch the next page.\n\t */\n\tcursor?: string;\n\t/**\n\t * Filter by field values, taxonomy terms, byline credits, or ranges.\n\t *\n\t * Taxonomy names are detected automatically and filtered via JOIN.\n\t * The reserved `byline` key filters by byline credit (any credit, not\n\t * just the primary one) via the `_emdash_content_bylines` junction\n\t * table; its value is one or more byline translation groups.\n\t * Other keys are treated as column filters on the content table.\n\t *\n\t * @example { category: 'news' } - taxonomy term\n\t * @example { byline: '01HXYZ...' } - entries credited to a byline (any position)\n\t * @example { series: 'main' } - exact match on a content field\n\t * @example { published_at: { gte: '2024-01-01', lt: '2025-01-01' } } - date range\n\t */\n\twhere?: Record<string, WhereValue>;\n\t/**\n\t * Order results by field(s)\n\t * @default { created_at: \"desc\" }\n\t */\n\torderBy?: OrderBySpec;\n\t/**\n\t * Filter by locale (e.g. 'en', 'fr').\n\t * When set, only returns content in this locale.\n\t */\n\tlocale?: string;\n}\n\n/**\n * Filter for loadEntry - type and id are required\n */\nexport interface EntryFilter {\n\ttype: string;\n\tid: string;\n\t/**\n\t * When set, fetch content data from this revision instead of the content table.\n\t * Used by preview mode to serve draft revision data.\n\t */\n\trevisionId?: string;\n\t/**\n\t * Locale to scope slug lookup. Only affects slug resolution;\n\t * IDs are globally unique and always resolve regardless of locale.\n\t */\n\tlocale?: string;\n}\n\n// Cached database instance (shared across calls)\nlet dbInstance: Kysely<Database> | null = null;\n\n/**\n * Get the database instance. Used by query wrapper functions and middleware.\n *\n * Checks the ALS request context first — if a per-request DB override is set\n * (e.g. by DO preview middleware), it takes precedence over the module-level\n * cached instance. This allows preview mode to route queries to an isolated\n * Durable Object database without modifying any calling code.\n *\n * Initializes the default database on first call using config from virtual module.\n */\nexport async function getDb(): Promise<Kysely<Database>> {\n\t// Per-request DB override via ALS (normal mode)\n\tconst ctx = getRequestContext();\n\tif (ctx?.db) {\n\t\treturn ctx.db as Kysely<Database>; // eslint-disable-line typescript/no-unsafe-type-assertion -- db is typed as unknown in RequestContext to avoid circular deps\n\t}\n\n\tif (!dbInstance) {\n\t\tawait loadVirtualModules();\n\t\tif (!virtualConfig?.database || typeof virtualCreateDialect !== \"function\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"EmDash database not configured. Add database config to emdash() in astro.config.mjs\",\n\t\t\t);\n\t\t}\n\t\tconst dialect = virtualCreateDialect(virtualConfig.database.config);\n\t\tdbInstance = new Kysely<Database>({ dialect, log: kyselyLogOption() });\n\t}\n\treturn dbInstance;\n}\n\n/**\n * Create an EmDash Live Collections loader\n *\n * This loader handles ALL content types in a single Astro collection.\n * Use `getEmDashCollection()` and `getEmDashEntry()` to query\n * specific content types.\n *\n * Database is configured in astro.config.mjs via the emdash() integration.\n *\n * @example\n * ```ts\n * // src/live.config.ts\n * import { defineLiveCollection } from \"astro:content\";\n * import { emdashLoader } from \"emdash\";\n *\n * export const collections = {\n * emdash: defineLiveCollection({\n * loader: emdashLoader(),\n * }),\n * };\n * ```\n */\nexport function emdashLoader(): LiveLoader<EntryData, EntryFilter, CollectionFilter> {\n\treturn {\n\t\tname: \"emdash\",\n\n\t\t/**\n\t\t * Load all entries for a content type\n\t\t */\n\t\tasync loadCollection({ filter }) {\n\t\t\ttry {\n\t\t\t\t// Get DB instance (initializes on first use)\n\t\t\t\tconst db = await getDb();\n\n\t\t\t\t// Type filter is required\n\t\t\t\tconst type = filter?.type;\n\t\t\t\tif (!type) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: new Error(\n\t\t\t\t\t\t\t\"type filter is required. Use getEmDashCollection() instead of getLiveCollection() directly.\",\n\t\t\t\t\t\t),\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// Query the per-collection table (ec_posts, ec_products, etc.)\n\t\t\t\tconst tableName = getTableName(type);\n\n\t\t\t\t// Build query with dynamic table name\n\t\t\t\tconst status = filter?.status || \"published\";\n\t\t\t\tconst limit = filter?.limit;\n\t\t\t\tconst cursor = filter?.cursor;\n\t\t\t\tconst where = filter?.where;\n\t\t\t\tconst orderBy = filter?.orderBy;\n\t\t\t\tconst locale = filter?.locale;\n\n\t\t\t\t// Cursor pagination: over-fetch by 1 to detect next page\n\t\t\t\tconst fetchLimit = limit ? limit + 1 : undefined;\n\n\t\t\t\t// Build cursor condition if cursor is provided\n\t\t\t\tconst cursorCondition = cursor ? buildCursorCondition(cursor, orderBy) : null;\n\n\t\t\t\t// Separate taxonomy / byline filters from field filters\n\t\t\t\tlet result: { rows: Record<string, unknown>[] };\n\t\t\t\t// Taxonomy filters AND together: each entry constrains the base\n\t\t\t\t// row to match at least one of its slugs *within that taxonomy*.\n\t\t\t\t// Term slugs are unique only within a taxonomy, so every filter\n\t\t\t\t// keeps its own `name` and emits its own `EXISTS` clause rather\n\t\t\t\t// than pooling slugs into one `IN`.\n\t\t\t\tconst taxonomyFilters: { name: string; slugs: string[] }[] = [];\n\t\t\t\t// A byline filter matches entries credited to any of the given\n\t\t\t\t// byline translation groups via the `_emdash_content_bylines`\n\t\t\t\t// junction table. `null` means no byline filter; an empty\n\t\t\t\t// `groups` array means the filter was requested but matches\n\t\t\t\t// nothing (short-circuited to an empty result below).\n\t\t\t\tlet bylineFilter: { groups: string[] } | null = null;\n\t\t\t\tconst fieldFilters: Record<string, WhereValue> = {};\n\n\t\t\t\tif (where && Object.keys(where).length > 0) {\n\t\t\t\t\tconst taxNames = await getTaxonomyNames(db);\n\n\t\t\t\t\tfor (const [key, value] of Object.entries(where)) {\n\t\t\t\t\t\tif (value == null) continue;\n\t\t\t\t\t\tif (key === \"byline\") {\n\t\t\t\t\t\t\tif (isWhereRange(value)) {\n\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\t`[emdash] where filter: range operators are not supported on \"byline\", ignored`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst groups = Array.isArray(value) ? value : [value];\n\t\t\t\t\t\t\tbylineFilter = { groups };\n\t\t\t\t\t\t} else if (taxNames.has(key)) {\n\t\t\t\t\t\t\tif (isWhereRange(value)) {\n\t\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t\t`[emdash] where filter: range operators are not supported on taxonomy \"${key}\", ignored`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst slugs = Array.isArray(value) ? value : [value];\n\t\t\t\t\t\t\ttaxonomyFilters.push({ name: key, slugs });\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfieldFilters[key] = value;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// A byline or taxonomy filter with no values matches nothing —\n\t\t\t\t// short-circuit before building SQL (an empty `IN ()` is invalid\n\t\t\t\t// SQL on both dialects).\n\t\t\t\tif (\n\t\t\t\t\t(bylineFilter && bylineFilter.groups.length === 0) ||\n\t\t\t\t\ttaxonomyFilters.some((f) => f.slugs.length === 0)\n\t\t\t\t) {\n\t\t\t\t\treturn { entries: [], cacheHint: { tags: [type] } };\n\t\t\t\t}\n\n\t\t\t\t{\n\t\t\t\t\t// Taxonomy and byline filters are applied as correlated\n\t\t\t\t\t// `EXISTS` semi-joins rather than `INNER JOIN ... DISTINCT`.\n\t\t\t\t\t// A join fan-out would force `SELECT DISTINCT table.*`, and\n\t\t\t\t\t// Postgres cannot apply DISTINCT to a row containing a `json`\n\t\t\t\t\t// column (no equality operator), so the join approach throws\n\t\t\t\t\t// there. EXISTS matches \"credited/tagged at least once\"\n\t\t\t\t\t// without duplicating rows, needs no DISTINCT, and works on\n\t\t\t\t\t// both SQLite and Postgres. The base query stays a single-\n\t\t\t\t\t// table `SELECT *`, so all field/status/locale/cursor/order\n\t\t\t\t\t// conditions reference unprefixed columns as before.\n\t\t\t\t\tconst orderByClause = buildOrderByClause(orderBy);\n\t\t\t\t\tconst statusCondition = buildStatusCondition(db, status);\n\t\t\t\t\tconst localeFilter = locale ? sql`AND locale = ${locale}` : sql``;\n\t\t\t\t\tconst cursorCond = cursorCondition ? sql`AND ${cursorCondition}` : sql``;\n\t\t\t\t\tconst fieldConds = buildFieldConditions(fieldFilters);\n\t\t\t\t\tconst fieldCondsSQL =\n\t\t\t\t\t\tfieldConds.length > 0 ? sql`${sql.join(fieldConds, sql` AND `)}` : null;\n\n\t\t\t\t\t// One `EXISTS` per taxonomy, AND'd together: an entry must be\n\t\t\t\t\t// tagged with a matching term in *every* requested taxonomy.\n\t\t\t\t\t// Each clause pins its own `t.name`, so slugs never pool\n\t\t\t\t\t// across taxonomies (they're only unique within one).\n\t\t\t\t\tconst taxonomyCond =\n\t\t\t\t\t\ttaxonomyFilters.length > 0\n\t\t\t\t\t\t\t? sql`${sql.join(\n\t\t\t\t\t\t\t\t\ttaxonomyFilters.map(\n\t\t\t\t\t\t\t\t\t\t(f) => sql`AND EXISTS (\n\t\t\t\t\t\t\tSELECT 1 FROM content_taxonomies ct\n\t\t\t\t\t\t\tINNER JOIN taxonomies t ON t.id = ct.taxonomy_id\n\t\t\t\t\t\t\tWHERE ct.collection = ${type}\n\t\t\t\t\t\t\t\tAND ct.entry_id = ${sql.ref(tableName)}.id\n\t\t\t\t\t\t\t\tAND t.name = ${f.name}\n\t\t\t\t\t\t\t\tAND t.slug IN (${sql.join(f.slugs.map((s) => sql`${s}`))})\n\t\t\t\t\t\t)`,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\tsql` `,\n\t\t\t\t\t\t\t\t)}`\n\t\t\t\t\t\t\t: sql``;\n\n\t\t\t\t\t// `_emdash_content_bylines.byline_id` stores the byline's\n\t\t\t\t\t// translation_group (migration 040), so a credit spans every\n\t\t\t\t\t// locale variant of the byline and we match the group directly.\n\t\t\t\t\tconst bylineCond = bylineFilter\n\t\t\t\t\t\t? sql`AND EXISTS (\n\t\t\t\t\t\t\tSELECT 1 FROM _emdash_content_bylines cb\n\t\t\t\t\t\t\tWHERE cb.collection_slug = ${type}\n\t\t\t\t\t\t\t\tAND cb.content_id = ${sql.ref(tableName)}.id\n\t\t\t\t\t\t\t\tAND cb.byline_id IN (${sql.join(bylineFilter.groups.map((g) => sql`${g}`))})\n\t\t\t\t\t\t)`\n\t\t\t\t\t\t: sql``;\n\n\t\t\t\t\tresult = await sql<Record<string, unknown>>`\n\t\t\t\t\t\tSELECT * FROM ${sql.ref(tableName)}\n\t\t\t\t\t\tWHERE deleted_at IS NULL\n\t\t\t\t\t\tAND ${statusCondition}\n\t\t\t\t\t\t${localeFilter}\n\t\t\t\t\t\t${cursorCond}\n\t\t\t\t\t\t${taxonomyCond}\n\t\t\t\t\t\t${bylineCond}\n\t\t\t\t\t\t${fieldCondsSQL ? sql`AND ${fieldCondsSQL}` : sql``}\n\t\t\t\t\t\t${orderByClause}\n\t\t\t\t\t\t${fetchLimit ? sql`LIMIT ${fetchLimit}` : sql``}\n\t\t\t\t\t`.execute(db);\n\t\t\t\t}\n\n\t\t\t\t// Detect whether there are more results (over-fetched by 1)\n\t\t\t\tconst hasMore = limit ? result.rows.length > limit : false;\n\t\t\t\tconst rows = hasMore ? result.rows.slice(0, limit) : result.rows;\n\n\t\t\t\t// Map rows to entries\n\t\t\t\tconst i18nConfig = virtualConfig?.i18n;\n\t\t\t\tconst i18nEnabled = i18nConfig && i18nConfig.locales.length > 1;\n\t\t\t\tconst entries = rows.map((row) => {\n\t\t\t\t\tconst slug = rowStr(row, \"slug\") || rowStr(row, \"id\");\n\t\t\t\t\tconst rowLocale = rowStr(row, \"locale\");\n\t\t\t\t\tconst shouldPrefix =\n\t\t\t\t\t\ti18nEnabled &&\n\t\t\t\t\t\trowLocale !== \"\" &&\n\t\t\t\t\t\t(rowLocale !== i18nConfig.defaultLocale || i18nConfig.prefixDefaultLocale);\n\t\t\t\t\tconst id = shouldPrefix ? `${rowLocale}/${slug}` : slug;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tslug: rowStr(row, \"slug\"),\n\t\t\t\t\t\tstatus: rowStr(row, \"status\", \"draft\"),\n\t\t\t\t\t\tdata: mapRowToData(row),\n\t\t\t\t\t\tcacheHint: {\n\t\t\t\t\t\t\ttags: [rowStr(row, \"id\")],\n\t\t\t\t\t\t\tlastModified: row.updated_at ? new Date(rowStr(row, \"updated_at\")) : undefined,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\t\t// Encode nextCursor from the last row if there are more results\n\t\t\t\tlet nextCursor: string | undefined;\n\t\t\t\tif (hasMore && rows.length > 0) {\n\t\t\t\t\tconst lastRow = rows.at(-1)!;\n\t\t\t\t\tconst primary = getPrimarySort(orderBy);\n\t\t\t\t\t// Strip table prefix from field name for row lookup\n\t\t\t\t\tconst fieldName = primary.field.includes(\".\")\n\t\t\t\t\t\t? primary.field.split(\".\").pop()!\n\t\t\t\t\t\t: primary.field;\n\t\t\t\t\tconst lastOrderValue = lastRow[fieldName];\n\t\t\t\t\tconst orderStr =\n\t\t\t\t\t\ttypeof lastOrderValue === \"string\" || typeof lastOrderValue === \"number\"\n\t\t\t\t\t\t\t? String(lastOrderValue)\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\tnextCursor = encodeCursor(orderStr, String(lastRow.id));\n\t\t\t\t}\n\n\t\t\t\t// Collection-level cache hint uses the most recent updated_at\n\t\t\t\tlet collectionLastModified: Date | undefined;\n\t\t\t\tfor (const row of rows) {\n\t\t\t\t\tif (row.updated_at) {\n\t\t\t\t\t\tconst d = new Date(rowStr(row, \"updated_at\"));\n\t\t\t\t\t\tif (!collectionLastModified || d > collectionLastModified) {\n\t\t\t\t\t\t\tcollectionLastModified = d;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tentries,\n\t\t\t\t\tnextCursor,\n\t\t\t\t\tcacheHint: {\n\t\t\t\t\t\ttags: [type],\n\t\t\t\t\t\tlastModified: collectionLastModified,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\t// Handle missing table/column gracefully - return empty collection.\n\t\t\t\t// Missing table happens before migrations have run.\n\t\t\t\t// Missing column happens when a where filter references a non-existent field.\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tif (isMissingTableError(error) || isMissingColumnError(error)) {\n\t\t\t\t\tif (isMissingColumnError(error)) {\n\t\t\t\t\t\tconsole.warn(`[emdash] where filter: ${message}`);\n\t\t\t\t\t}\n\t\t\t\t\treturn { entries: [] };\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\terror: new Error(`Failed to load collection: ${message}`),\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Load a single entry by type and ID/slug\n\t\t *\n\t\t * When filter.revisionId is set (preview mode), the entry's data\n\t\t * comes from the revisions table instead of the content table columns.\n\t\t */\n\t\tasync loadEntry({ filter }) {\n\t\t\ttry {\n\t\t\t\t// Get DB instance\n\t\t\t\tconst db = await getDb();\n\n\t\t\t\t// Both type and id are required\n\t\t\t\tconst type = filter?.type;\n\t\t\t\tconst id = filter?.id;\n\n\t\t\t\tif (!type || !id) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\terror: new Error(\n\t\t\t\t\t\t\t\"type and id filters are required. Use getEmDashEntry() instead of getLiveEntry() directly.\",\n\t\t\t\t\t\t),\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// Query the per-collection table\n\t\t\t\tconst tableName = getTableName(type);\n\t\t\t\tconst locale = filter?.locale;\n\n\t\t\t\t// Use raw SQL for dynamic table name, match by slug or id\n\t\t\t\t// When locale is specified, prefer locale-scoped slug match,\n\t\t\t\t// but IDs are globally unique so always check id without locale scope.\n\t\t\t\t//\n\t\t\t\t// LEFT JOIN _emdash_seo folds per-entry SEO (canonical, noindex,\n\t\t\t\t// etc.) into this single query at zero extra round-trip cost. The\n\t\t\t\t// joined columns are surfaced as a nested data.seo object via\n\t\t\t\t// extractSeo() and excluded from the generic field mapping. SEO is\n\t\t\t\t// 1:1 with content (PK on collection+content_id), so the join never\n\t\t\t\t// multiplies rows.\n\t\t\t\tconst seoSelect = sql.join(\n\t\t\t\t\tObject.entries(SEO_COLUMN_ALIASES).map(\n\t\t\t\t\t\t([col, alias]) => sql`${sql.ref(`s.${col}`)} AS ${sql.ref(alias)}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tconst result = locale\n\t\t\t\t\t? await sql<Record<string, unknown>>`\n\t\t\t\t\t\t\tSELECT c.*, ${seoSelect}\n\t\t\t\t\t\t\tFROM ${sql.ref(tableName)} AS c\n\t\t\t\t\t\t\tLEFT JOIN ${sql.ref(\"_emdash_seo\")} AS s\n\t\t\t\t\t\t\t\tON s.collection = ${type} AND s.content_id = c.id\n\t\t\t\t\t\t\tWHERE c.deleted_at IS NULL\n\t\t\t\t\t\t\tAND ((c.slug = ${id} AND c.locale = ${locale}) OR c.id = ${id})\n\t\t\t\t\t\t\tLIMIT 1\n\t\t\t\t\t\t`.execute(db)\n\t\t\t\t\t: await sql<Record<string, unknown>>`\n\t\t\t\t\t\t\tSELECT c.*, ${seoSelect}\n\t\t\t\t\t\t\tFROM ${sql.ref(tableName)} AS c\n\t\t\t\t\t\t\tLEFT JOIN ${sql.ref(\"_emdash_seo\")} AS s\n\t\t\t\t\t\t\t\tON s.collection = ${type} AND s.content_id = c.id\n\t\t\t\t\t\t\tWHERE c.deleted_at IS NULL\n\t\t\t\t\t\t\tAND (c.slug = ${id} OR c.id = ${id})\n\t\t\t\t\t\t\tLIMIT 1\n\t\t\t\t\t\t`.execute(db);\n\n\t\t\t\tconst row = result.rows[0];\n\t\t\t\tif (!row) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\tconst i18nConfig = virtualConfig?.i18n;\n\t\t\t\tconst i18nEnabled = i18nConfig && i18nConfig.locales.length > 1;\n\t\t\t\tconst entrySlug = rowStr(row, \"slug\") || rowStr(row, \"id\");\n\t\t\t\tconst entryLocale = rowStr(row, \"locale\");\n\t\t\t\tconst shouldPrefixEntry =\n\t\t\t\t\ti18nEnabled &&\n\t\t\t\t\tentryLocale !== \"\" &&\n\t\t\t\t\t(entryLocale !== i18nConfig.defaultLocale || i18nConfig.prefixDefaultLocale);\n\t\t\t\tconst entryId = shouldPrefixEntry ? `${entryLocale}/${entrySlug}` : entrySlug;\n\n\t\t\t\t// Preview mode: override content fields with revision data,\n\t\t\t\t// keeping system metadata from the content table row.\n\t\t\t\tconst revisionId = filter?.revisionId;\n\t\t\t\tif (revisionId) {\n\t\t\t\t\tconst revRow = await sql<{ data: string }>`\n\t\t\t\t\t\tSELECT data FROM revisions\n\t\t\t\t\t\tWHERE id = ${revisionId}\n\t\t\t\t\t\tLIMIT 1\n\t\t\t\t\t`.execute(db);\n\n\t\t\t\t\tconst revData = revRow.rows[0];\n\t\t\t\t\tif (revData) {\n\t\t\t\t\t\tconst parsed: Record<string, unknown> = JSON.parse(revData.data);\n\t\t\t\t\t\t// System metadata from content table + content fields from revision\n\t\t\t\t\t\tconst systemData: Record<string, unknown> = {};\n\t\t\t\t\t\tfor (const [key, mappedKey] of Object.entries(INCLUDE_IN_DATA)) {\n\t\t\t\t\t\t\tif (key in row) {\n\t\t\t\t\t\t\t\tif (DATE_COLUMNS.has(key)) {\n\t\t\t\t\t\t\t\t\tsystemData[mappedKey] = typeof row[key] === \"string\" ? new Date(row[key]) : null;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tsystemData[mappedKey] = row[key];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Use slug from revision metadata if present, else from content table\n\t\t\t\t\t\tconst slug = typeof parsed._slug === \"string\" ? parsed._slug : rowStr(row, \"slug\");\n\t\t\t\t\t\tconst revSlug = slug || rowStr(row, \"id\");\n\t\t\t\t\t\tconst revLocale = rowStr(row, \"locale\");\n\t\t\t\t\t\tconst shouldPrefixRev =\n\t\t\t\t\t\t\ti18nEnabled &&\n\t\t\t\t\t\t\trevLocale !== \"\" &&\n\t\t\t\t\t\t\t(revLocale !== i18nConfig.defaultLocale || i18nConfig.prefixDefaultLocale);\n\t\t\t\t\t\tconst revId = shouldPrefixRev ? `${revLocale}/${revSlug}` : revSlug;\n\t\t\t\t\t\t// SEO is not revisioned — it comes from the content row's\n\t\t\t\t\t\t// joined _emdash_seo columns, not the revision snapshot.\n\t\t\t\t\t\tconst revEntryData: Record<string, unknown> = {\n\t\t\t\t\t\t\t...systemData,\n\t\t\t\t\t\t\tslug,\n\t\t\t\t\t\t\t...mapRevisionData(parsed),\n\t\t\t\t\t\t};\n\t\t\t\t\t\tconst revSeo = extractSeo(row);\n\t\t\t\t\t\tif (revSeo) revEntryData.seo = revSeo;\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tid: revId,\n\t\t\t\t\t\t\tslug,\n\t\t\t\t\t\t\tstatus: rowStr(row, \"status\", \"draft\"),\n\t\t\t\t\t\t\tdata: revEntryData,\n\t\t\t\t\t\t\tcacheHint: {\n\t\t\t\t\t\t\t\ttags: [rowStr(row, \"id\")],\n\t\t\t\t\t\t\t\tlastModified: row.updated_at ? new Date(rowStr(row, \"updated_at\")) : undefined,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst entryData = mapRowToData(row);\n\t\t\t\tconst entrySeo = extractSeo(row);\n\t\t\t\tif (entrySeo) entryData.seo = entrySeo;\n\t\t\t\treturn {\n\t\t\t\t\tid: entryId,\n\t\t\t\t\tslug: rowStr(row, \"slug\"),\n\t\t\t\t\tstatus: rowStr(row, \"status\", \"draft\"),\n\t\t\t\t\tdata: entryData,\n\t\t\t\t\tcacheHint: {\n\t\t\t\t\t\ttags: [rowStr(row, \"id\")],\n\t\t\t\t\t\tlastModified: row.updated_at ? new Date(rowStr(row, \"updated_at\")) : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\t// Handle missing table gracefully - return undefined (not found).\n\t\t\t\t// This happens before migrations have run.\n\t\t\t\tif (isMissingTableError(error)) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\treturn {\n\t\t\t\t\terror: new Error(`Failed to load entry: ${message}`),\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"menus-DX4_E01q.mjs","names":[],"sources":["../src/database/repositories/menu.ts","../src/api/handlers/menus.ts"],"sourcesContent":["/**\n * Menu repository\n *\n * Owns every SQL touch for `_emdash_menus` and `_emdash_menu_items`, plus the\n * row→entity mapping. Matches the architecture used by every other resource\n * (content, taxonomies, redirects, comments, media): handlers stay thin and\n * orchestrate; the repository is the single place where snake_case DB columns\n * become camelCase entities (and vice versa).\n *\n * i18n: menus are per-locale. `(name, locale)` is unique. Translations of the\n * same menu share a `translation_group` ULID. Menu item `reference_id` stores\n * the referenced content's translation_group (not a specific row id) so a\n * single menu item survives content translations.\n */\n\nimport type { Kysely, Selectable } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { withTransaction } from \"../transaction.js\";\nimport type { Database, MenuItemTable, MenuTable } from \"../types.js\";\n\n/**\n * Thrown from inside a repository transaction when the menu the caller\n * resolved earlier has since been deleted. Handlers translate this to a\n * `NOT_FOUND` API response. Necessary because D1 disables FK enforcement\n * (so `ON DELETE CASCADE` won't fire), and an unchecked `setItems` would\n * happily insert items whose `menu_id` no longer exists, leaving orphans.\n */\nexport class MenuGoneError extends Error {\n\tconstructor(public readonly menuId: string) {\n\t\tsuper(`Menu ${menuId} was deleted while being modified`);\n\t\tthis.name = \"MenuGoneError\";\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Entity shapes (camelCase — what the API returns)\n// ---------------------------------------------------------------------------\n\nexport interface Menu {\n\tid: string;\n\tname: string;\n\tlabel: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\tlocale: string;\n\ttranslationGroup: string | null;\n}\n\nexport interface MenuItem {\n\tid: string;\n\tmenuId: string;\n\tparentId: string | null;\n\tsortOrder: number;\n\ttype: string;\n\treferenceCollection: string | null;\n\treferenceId: string | null;\n\tcustomUrl: string | null;\n\tlabel: string;\n\ttitleAttr: string | null;\n\ttarget: string | null;\n\tcssClasses: string | null;\n\tcreatedAt: string;\n\tlocale: string;\n\ttranslationGroup: string | null;\n}\n\nexport interface MenuListItem extends Menu {\n\titemCount: number;\n}\n\nexport interface MenuWithItems extends Menu {\n\titems: MenuItem[];\n}\n\nexport interface MenuTranslation {\n\tid: string;\n\tname: string;\n\tlabel: string;\n\tlocale: string;\n\tupdatedAt: string;\n}\n\n// ---------------------------------------------------------------------------\n// Input shapes\n// ---------------------------------------------------------------------------\n\nexport interface CreateMenuInput {\n\tname: string;\n\tlabel: string;\n\tlocale?: string;\n\t/**\n\t * When set, the new menu joins the source menu's translation_group and\n\t * inherits its items (cloned, with new ULIDs but the same translation_group\n\t * per item so nav entries stay logically identified across translations).\n\t */\n\ttranslationOf?: string;\n}\n\nexport interface UpdateMenuInput {\n\tlabel?: string;\n}\n\nexport interface CreateMenuItemInput {\n\ttype: string;\n\tlabel: string;\n\treferenceCollection?: string;\n\treferenceId?: string;\n\tcustomUrl?: string;\n\ttarget?: string;\n\ttitleAttr?: string;\n\tcssClasses?: string;\n\tparentId?: string;\n\tsortOrder?: number;\n}\n\nexport interface UpdateMenuItemInput {\n\tlabel?: string;\n\tcustomUrl?: string;\n\ttarget?: string;\n\ttitleAttr?: string;\n\tcssClasses?: string;\n\tparentId?: string | null;\n\tsortOrder?: number;\n}\n\n/**\n * Item shape used by `setItems()`. Items are placed by array order. Children\n * point at parents via `parentIndex` (must reference an earlier index, so the\n * insert can resolve parents before children). The validation of that ordering\n * lives at the API boundary (`handleMenuSetItems`) so REST/MCP callers receive\n * the same error shape.\n */\nexport interface SetMenuItem {\n\tlabel: string;\n\ttype: \"custom\" | \"page\" | \"post\" | \"taxonomy\" | \"collection\";\n\tcustomUrl?: string;\n\treferenceCollection?: string;\n\treferenceId?: string;\n\ttitleAttr?: string;\n\ttarget?: string;\n\tcssClasses?: string;\n\tparentIndex?: number;\n}\n\nexport interface ReorderItem {\n\tid: string;\n\tparentId: string | null;\n\tsortOrder: number;\n}\n\n// ---------------------------------------------------------------------------\n// Row → entity mappers\n// ---------------------------------------------------------------------------\n\nfunction rowToMenu(row: Selectable<MenuTable>): Menu {\n\treturn {\n\t\tid: row.id,\n\t\tname: row.name,\n\t\tlabel: row.label,\n\t\tcreatedAt: row.created_at,\n\t\tupdatedAt: row.updated_at,\n\t\tlocale: row.locale,\n\t\ttranslationGroup: row.translation_group,\n\t};\n}\n\nfunction rowToMenuItem(row: Selectable<MenuItemTable>): MenuItem {\n\treturn {\n\t\tid: row.id,\n\t\tmenuId: row.menu_id,\n\t\tparentId: row.parent_id,\n\t\tsortOrder: row.sort_order,\n\t\ttype: row.type,\n\t\treferenceCollection: row.reference_collection,\n\t\treferenceId: row.reference_id,\n\t\tcustomUrl: row.custom_url,\n\t\tlabel: row.label,\n\t\ttitleAttr: row.title_attr,\n\t\ttarget: row.target,\n\t\tcssClasses: row.css_classes,\n\t\tcreatedAt: row.created_at,\n\t\tlocale: row.locale,\n\t\ttranslationGroup: row.translation_group,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Repository\n// ---------------------------------------------------------------------------\n\nexport class MenuRepository {\n\tconstructor(private db: Kysely<Database>) {}\n\n\t// --- Menus -------------------------------------------------------------\n\n\t/**\n\t * List menus with their item counts. When `locale` is omitted, returns\n\t * every locale variant as its own row (consistent with the admin listing\n\t * model: each translation is its own menu for editing purposes).\n\t */\n\tasync findMany(options: { locale?: string } = {}): Promise<MenuListItem[]> {\n\t\t// Single LEFT JOIN + GROUP BY for the per-menu count. Avoids N+1.\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_menus as m\")\n\t\t\t.leftJoin(\"_emdash_menu_items as i\", \"i.menu_id\", \"m.id\")\n\t\t\t.select(({ fn }) => [\n\t\t\t\t\"m.id\",\n\t\t\t\t\"m.name\",\n\t\t\t\t\"m.label\",\n\t\t\t\t\"m.created_at\",\n\t\t\t\t\"m.updated_at\",\n\t\t\t\t\"m.locale\",\n\t\t\t\t\"m.translation_group\",\n\t\t\t\tfn.count<number>(\"i.id\").as(\"itemCount\"),\n\t\t\t])\n\t\t\t.groupBy([\n\t\t\t\t\"m.id\",\n\t\t\t\t\"m.name\",\n\t\t\t\t\"m.label\",\n\t\t\t\t\"m.created_at\",\n\t\t\t\t\"m.updated_at\",\n\t\t\t\t\"m.locale\",\n\t\t\t\t\"m.translation_group\",\n\t\t\t])\n\t\t\t.orderBy(\"m.name\", \"asc\");\n\t\tif (options.locale !== undefined) query = query.where(\"m.locale\", \"=\", options.locale);\n\t\tconst rows = await query.execute();\n\n\t\treturn rows.map((row) => ({\n\t\t\t// Postgres returns count() as `string`; SQLite as `number`. Normalize.\n\t\t\titemCount: typeof row.itemCount === \"string\" ? Number(row.itemCount) : row.itemCount,\n\t\t\t...rowToMenu({\n\t\t\t\tid: row.id,\n\t\t\t\tname: row.name,\n\t\t\t\tlabel: row.label,\n\t\t\t\tcreated_at: row.created_at,\n\t\t\t\tupdated_at: row.updated_at,\n\t\t\t\tlocale: row.locale,\n\t\t\t\ttranslation_group: row.translation_group,\n\t\t\t}),\n\t\t}));\n\t}\n\n\t/**\n\t * Find every menu row matching `name` (one per locale on multi-locale\n\t * installs). Callers use this both to look up a single menu (when locale\n\t * is supplied) and to detect AMBIGUOUS_LOCALE situations (`length > 1`).\n\t */\n\tasync findByName(name: string, options: { locale?: string } = {}): Promise<Menu[]> {\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_menus\")\n\t\t\t.selectAll()\n\t\t\t.where(\"name\", \"=\", name)\n\t\t\t.orderBy(\"locale\", \"asc\");\n\t\tif (options.locale !== undefined) query = query.where(\"locale\", \"=\", options.locale);\n\t\tconst rows = await query.execute();\n\t\treturn rows.map(rowToMenu);\n\t}\n\n\tasync findById(id: string): Promise<Menu | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_menus\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\t\treturn row ? rowToMenu(row) : null;\n\t}\n\n\t/** Fetch a menu plus its items, ordered by `sort_order`. */\n\tasync findWithItems(menuId: string): Promise<MenuWithItems | null> {\n\t\tconst menu = await this.findById(menuId);\n\t\tif (!menu) return null;\n\t\tconst items = await this.findItems(menuId);\n\t\treturn { ...menu, items };\n\t}\n\n\tasync findItems(menuId: string): Promise<MenuItem[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_menu_items\")\n\t\t\t.selectAll()\n\t\t\t.where(\"menu_id\", \"=\", menuId)\n\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t.execute();\n\t\treturn rows.map(rowToMenuItem);\n\t}\n\n\t/**\n\t * Returns true when a menu already exists for the given `(name, locale)`.\n\t * Used by the handler to surface a CONFLICT before attempting the insert.\n\t */\n\tasync existsByNameAndLocale(name: string, locale: string): Promise<boolean> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_menus\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"name\", \"=\", name)\n\t\t\t.where(\"locale\", \"=\", locale)\n\t\t\t.executeTakeFirst();\n\t\treturn row !== undefined;\n\t}\n\n\t/**\n\t * Create a menu. When `translationOf` is supplied the new menu joins the\n\t * source menu's translation_group and clones its items (each clone gets a\n\t * fresh ULID, but inherits the source item's `translation_group` so a\n\t * given nav entry resolves to \"the same item\" across menu translations).\n\t *\n\t * If the source menu is missing this throws — callers should validate\n\t * existence via `findById` first to return a clean NOT_FOUND.\n\t */\n\tasync create(input: CreateMenuInput): Promise<Menu> {\n\t\tconst id = ulid();\n\n\t\tlet translationGroup: string = id;\n\t\tlet sourceMenuId: string | null = null;\n\t\tif (input.translationOf) {\n\t\t\tconst source = await this.findById(input.translationOf);\n\t\t\tif (!source) throw new Error(\"Source menu for translation not found\");\n\t\t\ttranslationGroup = source.translationGroup ?? source.id;\n\t\t\tsourceMenuId = source.id;\n\t\t}\n\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tawait trx\n\t\t\t\t.insertInto(\"_emdash_menus\")\n\t\t\t\t.values({\n\t\t\t\t\tid,\n\t\t\t\t\tname: input.name,\n\t\t\t\t\tlabel: input.label,\n\t\t\t\t\t...(input.locale !== undefined ? { locale: input.locale } : {}),\n\t\t\t\t\ttranslation_group: translationGroup,\n\t\t\t\t})\n\t\t\t\t.execute();\n\n\t\t\tif (sourceMenuId) {\n\t\t\t\tconst sourceItems = await trx\n\t\t\t\t\t.selectFrom(\"_emdash_menu_items\")\n\t\t\t\t\t.selectAll()\n\t\t\t\t\t.where(\"menu_id\", \"=\", sourceMenuId)\n\t\t\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t\t\t.execute();\n\t\t\t\tif (sourceItems.length > 0) {\n\t\t\t\t\t// old-id → new-id map so parent pointers land on the clones.\n\t\t\t\t\tconst idMap = new Map<string, string>();\n\t\t\t\t\tfor (const item of sourceItems) idMap.set(item.id, ulid());\n\n\t\t\t\t\tawait trx\n\t\t\t\t\t\t.insertInto(\"_emdash_menu_items\")\n\t\t\t\t\t\t.values(\n\t\t\t\t\t\t\tsourceItems.map((item) => ({\n\t\t\t\t\t\t\t\tid: idMap.get(item.id)!,\n\t\t\t\t\t\t\t\tmenu_id: id,\n\t\t\t\t\t\t\t\tparent_id: item.parent_id ? (idMap.get(item.parent_id) ?? null) : null,\n\t\t\t\t\t\t\t\tsort_order: item.sort_order,\n\t\t\t\t\t\t\t\ttype: item.type,\n\t\t\t\t\t\t\t\treference_collection: item.reference_collection,\n\t\t\t\t\t\t\t\treference_id: item.reference_id,\n\t\t\t\t\t\t\t\tcustom_url: item.custom_url,\n\t\t\t\t\t\t\t\tlabel: item.label,\n\t\t\t\t\t\t\t\ttitle_attr: item.title_attr,\n\t\t\t\t\t\t\t\ttarget: item.target,\n\t\t\t\t\t\t\t\tcss_classes: item.css_classes,\n\t\t\t\t\t\t\t\t...(input.locale !== undefined ? { locale: input.locale } : {}),\n\t\t\t\t\t\t\t\ttranslation_group: item.translation_group ?? item.id,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.execute();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tconst created = await this.findById(id);\n\t\tif (!created) throw new Error(\"Failed to create menu\");\n\t\treturn created;\n\t}\n\n\tasync update(id: string, input: UpdateMenuInput): Promise<Menu | null> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) return null;\n\n\t\tconst values: Record<string, unknown> = {};\n\t\tif (input.label !== undefined) values.label = input.label;\n\n\t\tif (Object.keys(values).length > 0) {\n\t\t\tawait this.db.updateTable(\"_emdash_menus\").set(values).where(\"id\", \"=\", id).execute();\n\t\t}\n\n\t\treturn (await this.findById(id))!;\n\t}\n\n\t/**\n\t * Delete a menu. Items are deleted explicitly to avoid relying on the\n\t * `ON DELETE CASCADE` FK declared in migration 005, which migration 036\n\t * removed: that FK is what made #1021 destructive on D1 (the cascade\n\t * fired when the i18n migration dropped `_emdash_menus`), so dropping\n\t * the FK was the fix. The explicit delete keeps the runtime working\n\t * the same way before and after the migration.\n\t */\n\tasync delete(id: string): Promise<boolean> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) return false;\n\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tawait trx.deleteFrom(\"_emdash_menu_items\").where(\"menu_id\", \"=\", id).execute();\n\t\t\tawait trx.deleteFrom(\"_emdash_menus\").where(\"id\", \"=\", id).execute();\n\t\t});\n\t\treturn true;\n\t}\n\n\t/**\n\t * List every translation of a menu (by id or translation_group).\n\t *\n\t * Returns `null` when neither the id nor the group resolves to a menu,\n\t * mapped to NOT_FOUND by the handler.\n\t */\n\tasync listTranslations(\n\t\tidOrGroup: string,\n\t): Promise<{ translationGroup: string | null; translations: MenuTranslation[] } | null> {\n\t\tconst anchor = await this.db\n\t\t\t.selectFrom(\"_emdash_menus\")\n\t\t\t.selectAll()\n\t\t\t.where((eb) => eb.or([eb(\"id\", \"=\", idOrGroup), eb(\"translation_group\", \"=\", idOrGroup)]))\n\t\t\t.executeTakeFirst();\n\t\tif (!anchor) return null;\n\n\t\tconst group = anchor.translation_group ?? anchor.id;\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_menus\")\n\t\t\t.selectAll()\n\t\t\t.where(\"translation_group\", \"=\", group)\n\t\t\t.orderBy(\"locale\", \"asc\")\n\t\t\t.execute();\n\n\t\treturn {\n\t\t\ttranslationGroup: group,\n\t\t\ttranslations: rows.map((row) => ({\n\t\t\t\tid: row.id,\n\t\t\t\tname: row.name,\n\t\t\t\tlocale: row.locale,\n\t\t\t\tlabel: row.label,\n\t\t\t\tupdatedAt: row.updated_at,\n\t\t\t})),\n\t\t};\n\t}\n\n\t// --- Items -------------------------------------------------------------\n\n\t/**\n\t * Insert a menu item. `locale` is propagated from the parent menu so\n\t * `_emdash_menu_items.locale` mirrors the menu's locale (queries can scope\n\t * by locale without a join).\n\t *\n\t * When `sortOrder` is omitted, the next position within the same parent\n\t * scope is used (max + 1). The fresh `translation_group` defaults to the\n\t * item's own id, matching the migration 036 backfill.\n\t */\n\tasync createItem(menuId: string, locale: string, input: CreateMenuItemInput): Promise<MenuItem> {\n\t\tlet sortOrder = input.sortOrder ?? 0;\n\t\tif (input.sortOrder === undefined) {\n\t\t\tconst maxOrder = await this.db\n\t\t\t\t.selectFrom(\"_emdash_menu_items\")\n\t\t\t\t.select(({ fn }) => fn.max(\"sort_order\").as(\"max\"))\n\t\t\t\t.where(\"menu_id\", \"=\", menuId)\n\t\t\t\t.where(\"parent_id\", \"is\", input.parentId ?? null)\n\t\t\t\t.executeTakeFirst();\n\t\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- Kysely fn.max returns unknown; always a number for sort_order column\n\t\t\tsortOrder = ((maxOrder?.max as number) ?? -1) + 1;\n\t\t}\n\n\t\tconst id = ulid();\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_menu_items\")\n\t\t\t.values({\n\t\t\t\tid,\n\t\t\t\tmenu_id: menuId,\n\t\t\t\tparent_id: input.parentId ?? null,\n\t\t\t\tsort_order: sortOrder,\n\t\t\t\ttype: input.type,\n\t\t\t\treference_collection: input.referenceCollection ?? null,\n\t\t\t\treference_id: input.referenceId ?? null,\n\t\t\t\tcustom_url: input.customUrl ?? null,\n\t\t\t\tlabel: input.label,\n\t\t\t\ttitle_attr: input.titleAttr ?? null,\n\t\t\t\ttarget: input.target ?? null,\n\t\t\t\tcss_classes: input.cssClasses ?? null,\n\t\t\t\tlocale,\n\t\t\t\ttranslation_group: id,\n\t\t\t})\n\t\t\t.execute();\n\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_menu_items\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirstOrThrow();\n\t\treturn rowToMenuItem(row);\n\t}\n\n\t/**\n\t * Update a menu item. Caller must ensure the item belongs to the menu —\n\t * the `where(\"menu_id\", \"=\", menuId)` guard prevents cross-menu writes.\n\t * Returns `null` if the item is not found within the menu.\n\t */\n\tasync updateItem(\n\t\tmenuId: string,\n\t\titemId: string,\n\t\tinput: UpdateMenuItemInput,\n\t): Promise<MenuItem | null> {\n\t\tconst existing = await this.db\n\t\t\t.selectFrom(\"_emdash_menu_items\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"id\", \"=\", itemId)\n\t\t\t.where(\"menu_id\", \"=\", menuId)\n\t\t\t.executeTakeFirst();\n\t\tif (!existing) return null;\n\n\t\tconst values: Record<string, unknown> = {};\n\t\tif (input.label !== undefined) values.label = input.label;\n\t\tif (input.customUrl !== undefined) values.custom_url = input.customUrl;\n\t\tif (input.target !== undefined) values.target = input.target;\n\t\tif (input.titleAttr !== undefined) values.title_attr = input.titleAttr;\n\t\tif (input.cssClasses !== undefined) values.css_classes = input.cssClasses;\n\t\tif (input.parentId !== undefined) values.parent_id = input.parentId;\n\t\tif (input.sortOrder !== undefined) values.sort_order = input.sortOrder;\n\n\t\tif (Object.keys(values).length > 0) {\n\t\t\tawait this.db\n\t\t\t\t.updateTable(\"_emdash_menu_items\")\n\t\t\t\t.set(values)\n\t\t\t\t.where(\"id\", \"=\", itemId)\n\t\t\t\t.execute();\n\t\t}\n\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_menu_items\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", itemId)\n\t\t\t.executeTakeFirstOrThrow();\n\t\treturn rowToMenuItem(row);\n\t}\n\n\t/** Delete an item scoped to its menu. Returns false if nothing was deleted. */\n\tasync deleteItem(menuId: string, itemId: string): Promise<boolean> {\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"_emdash_menu_items\")\n\t\t\t.where(\"id\", \"=\", itemId)\n\t\t\t.where(\"menu_id\", \"=\", menuId)\n\t\t\t.execute();\n\t\treturn result[0]?.numDeletedRows !== 0n;\n\t}\n\n\t/**\n\t * Atomic replace: delete every existing item and re-insert in order.\n\t * `parentIndex` (validated by the caller) is resolved against the live\n\t * insert order so children always reference real parent ids.\n\t *\n\t * Returns the count of inserted items (matches the existing handler API).\n\t */\n\tasync setItems(\n\t\tmenuId: string,\n\t\tlocale: string,\n\t\titems: SetMenuItem[],\n\t): Promise<{ itemCount: number }> {\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\t// Re-check menu existence INSIDE the transaction. The handler\n\t\t\t// resolved by (name, locale) before this call; if a concurrent\n\t\t\t// menu_delete landed in between, inserting new items would\n\t\t\t// silently orphan them. The FK from migration 005 was removed\n\t\t\t// by migration 036 (#1021) and not restored, so nothing at the\n\t\t\t// schema level stops the orphans. Throw a MenuGoneError so the\n\t\t\t// rollback fires and the handler returns NOT_FOUND with the\n\t\t\t// original menu name in the message.\n\t\t\tconst stillThere = await trx\n\t\t\t\t.selectFrom(\"_emdash_menus\")\n\t\t\t\t.select(\"id\")\n\t\t\t\t.where(\"id\", \"=\", menuId)\n\t\t\t\t.executeTakeFirst();\n\t\t\tif (!stillThere) throw new MenuGoneError(menuId);\n\n\t\t\tawait trx.deleteFrom(\"_emdash_menu_items\").where(\"menu_id\", \"=\", menuId).execute();\n\n\t\t\tconst insertedIds: string[] = [];\n\t\t\tfor (let i = 0; i < items.length; i++) {\n\t\t\t\tconst item = items[i];\n\t\t\t\tif (!item) continue;\n\t\t\t\tconst id = ulid();\n\t\t\t\tconst parentId =\n\t\t\t\t\titem.parentIndex !== undefined ? (insertedIds[item.parentIndex] ?? null) : null;\n\t\t\t\tawait trx\n\t\t\t\t\t.insertInto(\"_emdash_menu_items\")\n\t\t\t\t\t.values({\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tmenu_id: menuId,\n\t\t\t\t\t\tparent_id: parentId,\n\t\t\t\t\t\tsort_order: i,\n\t\t\t\t\t\ttype: item.type,\n\t\t\t\t\t\treference_collection: item.referenceCollection ?? null,\n\t\t\t\t\t\treference_id: item.referenceId ?? null,\n\t\t\t\t\t\tcustom_url: item.customUrl ?? null,\n\t\t\t\t\t\tlabel: item.label,\n\t\t\t\t\t\ttitle_attr: item.titleAttr ?? null,\n\t\t\t\t\t\ttarget: item.target ?? null,\n\t\t\t\t\t\tcss_classes: item.cssClasses ?? null,\n\t\t\t\t\t\tlocale,\n\t\t\t\t\t})\n\t\t\t\t\t.execute();\n\t\t\t\tinsertedIds.push(id);\n\t\t\t}\n\n\t\t\tawait trx\n\t\t\t\t.updateTable(\"_emdash_menus\")\n\t\t\t\t.set({ updated_at: new Date().toISOString() })\n\t\t\t\t.where(\"id\", \"=\", menuId)\n\t\t\t\t.execute();\n\t\t});\n\n\t\treturn { itemCount: items.length };\n\t}\n\n\t/**\n\t * Batch reorder items. Each entry is applied scoped to the menu so a\n\t * malicious payload cannot move foreign items into this menu's siblings.\n\t */\n\tasync reorderItems(menuId: string, items: ReorderItem[]): Promise<MenuItem[]> {\n\t\treturn withTransaction(this.db, async (trx) => {\n\t\t\tfor (const item of items) {\n\t\t\t\tawait trx\n\t\t\t\t\t.updateTable(\"_emdash_menu_items\")\n\t\t\t\t\t.set({ parent_id: item.parentId, sort_order: item.sortOrder })\n\t\t\t\t\t.where(\"id\", \"=\", item.id)\n\t\t\t\t\t.where(\"menu_id\", \"=\", menuId)\n\t\t\t\t\t.execute();\n\t\t\t}\n\n\t\t\tconst rows = await trx\n\t\t\t\t.selectFrom(\"_emdash_menu_items\")\n\t\t\t\t.selectAll()\n\t\t\t\t.where(\"menu_id\", \"=\", menuId)\n\t\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t\t.execute();\n\t\t\treturn rows.map(rowToMenuItem);\n\t\t});\n\t}\n}\n","/**\n * Menu CRUD handlers.\n *\n * Business logic for menu and menu-item endpoints. Routes are thin wrappers\n * that parse input, check auth, and call these.\n *\n * i18n: Menus are per-locale. `(name, locale)` is unique, so the same `name`\n * (e.g. \"primary\") can exist in several locales within one translation_group.\n * Menu items carry a `locale` + `translation_group` as well, and their\n * `reference_id` points at the referenced content's translation_group (not a\n * specific row id), so a single menu item target survives content translations.\n */\n\nimport type { Kysely } from \"kysely\";\n\nimport {\n\tMenuGoneError,\n\tMenuRepository,\n\ttype CreateMenuItemInput as CreateMenuItemRepoInput,\n\ttype Menu,\n\ttype MenuItem,\n\ttype MenuListItem,\n\ttype MenuWithItems,\n\ttype SetMenuItem,\n\ttype UpdateMenuItemInput as UpdateMenuItemRepoInput,\n} from \"../../database/repositories/menu.js\";\nimport type { Database } from \"../../database/types.js\";\nimport { getI18nConfig } from \"../../i18n/config.js\";\nimport type { ApiResult } from \"../types.js\";\n\n// Re-export entity types so route files and tests can import them from the\n// handler module without having to know about the repository layout.\nexport type {\n\tMenu,\n\tMenuItem,\n\tMenuListItem,\n\tMenuTranslation,\n\tMenuWithItems,\n} from \"../../database/repositories/menu.js\";\n\nexport interface MenuTranslationsResponse {\n\ttranslationGroup: string | null;\n\ttranslations: Array<{\n\t\tid: string;\n\t\tname: string;\n\t\tlocale: string;\n\t\tlabel: string;\n\t\tupdatedAt: string;\n\t}>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Error returned when a menu lookup by `name` matches multiple locale\n * variants and the caller did not pass `locale` to disambiguate. Maps to\n * HTTP 400 via `mapErrorStatus`. The available locales are surfaced in the\n * message so MCP/REST callers can recover by re-issuing with `locale`.\n */\nfunction ambiguousMenuLocaleError(\n\tname: string,\n\tlocales: readonly string[],\n): { success: false; error: { code: \"AMBIGUOUS_LOCALE\"; message: string } } {\n\tconst sortedLocales = locales.toSorted();\n\treturn {\n\t\tsuccess: false,\n\t\terror: {\n\t\t\tcode: \"AMBIGUOUS_LOCALE\",\n\t\t\tmessage: `Menu '${name}' exists in multiple locales (${sortedLocales.join(\n\t\t\t\t\", \",\n\t\t\t)}); pass 'locale' to disambiguate.`,\n\t\t},\n\t};\n}\n\ntype ResolveMenuResult =\n\t| { success: true; menu: Menu }\n\t| { success: false; error: { code: \"NOT_FOUND\" | \"AMBIGUOUS_LOCALE\"; message: string } };\n\n/**\n * Resolve a menu by name + optional locale to a single Menu, surfacing the\n * canonical NOT_FOUND / AMBIGUOUS_LOCALE errors. Every item handler relies on\n * this to translate (name, locale) into an unambiguous menu row.\n */\nasync function resolveMenu(\n\trepo: MenuRepository,\n\tname: string,\n\toptions: { locale?: string },\n): Promise<ResolveMenuResult> {\n\tconst matches = await repo.findByName(name, options);\n\tif (matches.length === 0) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\tmessage: `Menu '${name}' not found${options.locale ? ` in locale '${options.locale}'` : \"\"}`,\n\t\t\t},\n\t\t};\n\t}\n\tif (matches.length > 1) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: ambiguousMenuLocaleError(\n\t\t\t\tname,\n\t\t\t\tmatches.map((m) => m.locale),\n\t\t\t).error,\n\t\t};\n\t}\n\treturn { success: true, menu: matches[0] };\n}\n\n// ---------------------------------------------------------------------------\n// Menu handlers\n// ---------------------------------------------------------------------------\n\n/**\n * List menus with item counts. Filter by `locale` when provided.\n */\nexport async function handleMenuList(\n\tdb: Kysely<Database>,\n\toptions: { locale?: string } = {},\n): Promise<ApiResult<MenuListItem[]>> {\n\ttry {\n\t\tconst repo = new MenuRepository(db);\n\t\tconst items = await repo.findMany(options);\n\t\treturn { success: true, data: items };\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MENU_LIST_ERROR\", message: \"Failed to fetch menus\" },\n\t\t};\n\t}\n}\n\n/**\n * Create a new menu. When `translationOf` is supplied the new menu joins the\n * source menu's translation_group (and gets the source's items cloned by the\n * repository).\n */\nexport async function handleMenuCreate(\n\tdb: Kysely<Database>,\n\tinput: { name: string; label: string; locale?: string; translationOf?: string },\n): Promise<ApiResult<Menu>> {\n\ttry {\n\t\t// Translating from a source menu only makes sense when the caller\n\t\t// names the target locale: otherwise we'd silently clone into the\n\t\t// configured default, which is almost never what's intended (and\n\t\t// will collide if the source is already the default-locale menu).\n\t\t// Enforced here so REST/SDK callers get the same guard as MCP.\n\t\tif (input.translationOf && !input.locale) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"VALIDATION_ERROR\",\n\t\t\t\t\tmessage: \"`locale` is required when `translationOf` is provided\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst repo = new MenuRepository(db);\n\n\t\t// Existence check up front so the repo's \"Source not found\" throw\n\t\t// becomes a clean NOT_FOUND on the API.\n\t\tif (input.translationOf) {\n\t\t\tconst source = await repo.findById(input.translationOf);\n\t\t\tif (!source) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: { code: \"NOT_FOUND\", message: \"Source menu for translation not found\" },\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Duplicate guard: same (name, locale). Falls back to the configured\n\t\t// defaultLocale to match the column DEFAULT set by migration 036.\n\t\tconst effectiveLocale = input.locale ?? getI18nConfig()?.defaultLocale ?? \"en\";\n\t\tif (await repo.existsByNameAndLocale(input.name, effectiveLocale)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CONFLICT\",\n\t\t\t\t\tmessage: `Menu \"${input.name}\" already exists${\n\t\t\t\t\t\tinput.locale ? ` in locale \"${input.locale}\"` : \"\"\n\t\t\t\t\t}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst menu = await repo.create(input);\n\t\treturn { success: true, data: menu };\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MENU_CREATE_ERROR\", message: \"Failed to create menu\" },\n\t\t};\n\t}\n}\n\n/**\n * Get a single menu by name. Honours an optional `locale` filter; when two\n * menus share a name across locales, the locale distinguishes them.\n *\n * Historical behaviour: when `locale` is omitted, returns the lowest-locale\n * match (deterministic). Mirrors the pre-repo handler.\n */\nexport async function handleMenuGet(\n\tdb: Kysely<Database>,\n\tname: string,\n\toptions: { locale?: string } = {},\n): Promise<ApiResult<MenuWithItems>> {\n\ttry {\n\t\tconst repo = new MenuRepository(db);\n\t\tconst matches = await repo.findByName(name, options);\n\t\tif (matches.length === 0) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"NOT_FOUND\", message: `Menu '${name}' not found` },\n\t\t\t};\n\t\t}\n\t\tconst menu = matches[0];\n\t\tconst items = await repo.findItems(menu.id);\n\t\treturn { success: true, data: { ...menu, items } };\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MENU_GET_ERROR\", message: \"Failed to fetch menu\" },\n\t\t};\n\t}\n}\n\n/**\n * Get a menu by id. Useful when the caller already has the id (e.g. after\n * creating a translation and navigating to it).\n */\nexport async function handleMenuGetById(\n\tdb: Kysely<Database>,\n\tid: string,\n): Promise<ApiResult<MenuWithItems>> {\n\ttry {\n\t\tconst repo = new MenuRepository(db);\n\t\tconst menu = await repo.findWithItems(id);\n\t\tif (!menu) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"NOT_FOUND\", message: `Menu '${id}' not found` },\n\t\t\t};\n\t\t}\n\t\treturn { success: true, data: menu };\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MENU_GET_ERROR\", message: \"Failed to fetch menu\" },\n\t\t};\n\t}\n}\n\n/**\n * Update a menu's label. The name + locale are immutable.\n */\nexport async function handleMenuUpdate(\n\tdb: Kysely<Database>,\n\tname: string,\n\tinput: { label?: string; locale?: string },\n): Promise<ApiResult<Menu>> {\n\ttry {\n\t\tconst repo = new MenuRepository(db);\n\t\tconst resolved = await resolveMenu(repo, name, { locale: input.locale });\n\t\tif (!resolved.success) return resolved;\n\t\tconst updated = await repo.update(resolved.menu.id, { label: input.label });\n\t\tif (!updated) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"NOT_FOUND\", message: `Menu '${name}' not found` },\n\t\t\t};\n\t\t}\n\t\treturn { success: true, data: updated };\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MENU_UPDATE_ERROR\", message: \"Failed to update menu\" },\n\t\t};\n\t}\n}\n\n/**\n * Delete a menu (and its items, via the repository's explicit cleanup).\n */\nexport async function handleMenuDelete(\n\tdb: Kysely<Database>,\n\tname: string,\n\toptions: { locale?: string } = {},\n): Promise<ApiResult<{ deleted: true }>> {\n\ttry {\n\t\tconst repo = new MenuRepository(db);\n\t\tconst resolved = await resolveMenu(repo, name, options);\n\t\tif (!resolved.success) return resolved;\n\t\tawait repo.delete(resolved.menu.id);\n\t\treturn { success: true, data: { deleted: true } };\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MENU_DELETE_ERROR\", message: \"Failed to delete menu\" },\n\t\t};\n\t}\n}\n\n/**\n * List every translation of a menu (by id or translation_group).\n */\nexport async function handleMenuTranslations(\n\tdb: Kysely<Database>,\n\tidOrGroup: string,\n): Promise<ApiResult<MenuTranslationsResponse>> {\n\ttry {\n\t\tconst repo = new MenuRepository(db);\n\t\tconst result = await repo.listTranslations(idOrGroup);\n\t\tif (!result) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"NOT_FOUND\", message: \"Menu not found\" },\n\t\t\t};\n\t\t}\n\t\treturn { success: true, data: result };\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MENU_TRANSLATIONS_ERROR\", message: \"Failed to list menu translations\" },\n\t\t};\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Menu item handlers\n// ---------------------------------------------------------------------------\n\nexport type CreateMenuItemInput = CreateMenuItemRepoInput;\nexport type UpdateMenuItemInput = UpdateMenuItemRepoInput;\nexport type MenuSetItemsInput = SetMenuItem;\n\n/**\n * Add an item to a menu. The item inherits the menu's locale.\n */\nexport async function handleMenuItemCreate(\n\tdb: Kysely<Database>,\n\tmenuName: string,\n\tinput: CreateMenuItemInput,\n\toptions: { locale?: string } = {},\n): Promise<ApiResult<MenuItem>> {\n\ttry {\n\t\tconst repo = new MenuRepository(db);\n\t\tconst resolved = await resolveMenu(repo, menuName, options);\n\t\tif (!resolved.success) return resolved;\n\n\t\tconst item = await repo.createItem(resolved.menu.id, resolved.menu.locale, input);\n\t\treturn { success: true, data: item };\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MENU_ITEM_CREATE_ERROR\", message: \"Failed to create menu item\" },\n\t\t};\n\t}\n}\n\n/**\n * Update a menu item.\n */\nexport async function handleMenuItemUpdate(\n\tdb: Kysely<Database>,\n\tmenuName: string,\n\titemId: string,\n\tinput: UpdateMenuItemInput,\n\toptions: { locale?: string } = {},\n): Promise<ApiResult<MenuItem>> {\n\ttry {\n\t\tconst repo = new MenuRepository(db);\n\t\tconst resolved = await resolveMenu(repo, menuName, options);\n\t\tif (!resolved.success) return resolved;\n\n\t\tconst updated = await repo.updateItem(resolved.menu.id, itemId, input);\n\t\tif (!updated) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"NOT_FOUND\", message: \"Menu item not found\" },\n\t\t\t};\n\t\t}\n\t\treturn { success: true, data: updated };\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MENU_ITEM_UPDATE_ERROR\", message: \"Failed to update menu item\" },\n\t\t};\n\t}\n}\n\n/**\n * Delete a menu item.\n */\nexport async function handleMenuItemDelete(\n\tdb: Kysely<Database>,\n\tmenuName: string,\n\titemId: string,\n\toptions: { locale?: string } = {},\n): Promise<ApiResult<{ deleted: true }>> {\n\ttry {\n\t\tconst repo = new MenuRepository(db);\n\t\tconst resolved = await resolveMenu(repo, menuName, options);\n\t\tif (!resolved.success) return resolved;\n\n\t\tconst deleted = await repo.deleteItem(resolved.menu.id, itemId);\n\t\tif (!deleted) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"NOT_FOUND\", message: \"Menu item not found\" },\n\t\t\t};\n\t\t}\n\t\treturn { success: true, data: { deleted: true } };\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MENU_ITEM_DELETE_ERROR\", message: \"Failed to delete menu item\" },\n\t\t};\n\t}\n}\n\nexport interface ReorderItem {\n\tid: string;\n\tparentId: string | null;\n\tsortOrder: number;\n}\n\n// ---------------------------------------------------------------------------\n// Atomic-replace menu items (used by the MCP `menu_set_items` tool and admin)\n// ---------------------------------------------------------------------------\n\n/**\n * Replace the entire set of items for a menu in one atomic transaction.\n *\n * Existing items are deleted and the new list is inserted in the order\n * provided. `parentIndex` references resolve to actual parent IDs as the\n * insert proceeds.\n */\nexport async function handleMenuSetItems(\n\tdb: Kysely<Database>,\n\tmenuName: string,\n\titems: MenuSetItemsInput[],\n\toptions: { locale?: string } = {},\n): Promise<ApiResult<{ name: string; itemCount: number }>> {\n\t// Validate parentIndex references — must be strictly earlier so the array\n\t// can be inserted in order with parents resolved first. Negative indices\n\t// are caught by Zod's `.nonnegative()` at the MCP boundary, but we guard\n\t// explicitly so REST routes / direct handler use get the same error.\n\tfor (let i = 0; i < items.length; i++) {\n\t\tconst item = items[i];\n\t\tif (item?.parentIndex !== undefined) {\n\t\t\tif (item.parentIndex < 0 || item.parentIndex >= i) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"VALIDATION_ERROR\",\n\t\t\t\t\t\tmessage: `item[${i}].parentIndex (${item.parentIndex}) must reference an earlier item`,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\ttry {\n\t\tconst repo = new MenuRepository(db);\n\t\tconst resolved = await resolveMenu(repo, menuName, options);\n\t\tif (!resolved.success) return resolved;\n\n\t\tconst { itemCount } = await repo.setItems(resolved.menu.id, resolved.menu.locale, items);\n\t\treturn { success: true, data: { name: menuName, itemCount } };\n\t} catch (error) {\n\t\t// `MenuGoneError` is thrown from inside the repository transaction\n\t\t// when the menu was deleted concurrently between `resolveMenu` and the\n\t\t// setItems write. Returning NOT_FOUND mirrors the original handler's\n\t\t// in-transaction `notFoundSentinel` branch and keeps the response\n\t\t// shape stable for REST/MCP callers.\n\t\tif (error instanceof MenuGoneError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `Menu '${menuName}' not found${\n\t\t\t\t\t\toptions.locale ? ` in locale '${options.locale}'` : \"\"\n\t\t\t\t\t}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"[emdash] handleMenuSetItems failed:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MENU_SET_ITEMS_ERROR\", message: \"Failed to set menu items\" },\n\t\t};\n\t}\n}\n\n/**\n * Batch reorder menu items.\n */\nexport async function handleMenuItemReorder(\n\tdb: Kysely<Database>,\n\tmenuName: string,\n\titems: ReorderItem[],\n\toptions: { locale?: string } = {},\n): Promise<ApiResult<MenuItem[]>> {\n\ttry {\n\t\tconst repo = new MenuRepository(db);\n\t\tconst resolved = await resolveMenu(repo, menuName, options);\n\t\tif (!resolved.success) return resolved;\n\n\t\tconst updated = await repo.reorderItems(resolved.menu.id, items);\n\t\treturn { success: true, data: updated };\n\t} catch {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MENU_REORDER_ERROR\", message: \"Failed to reorder menu items\" },\n\t\t};\n\t}\n}\n"],"mappings":"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"}
|