emdash 0.24.1 → 0.26.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/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +12 -11
- package/dist/api/route-utils.mjs.map +1 -1
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/api/schemas/index.mjs +2 -2
- package/dist/{api-d_0wi9fW.mjs → api-BMfveMoB.mjs} +24 -20
- package/dist/api-BMfveMoB.mjs.map +1 -0
- package/dist/{apply-BE9UaWX0.mjs → apply--IVNSdXn.mjs} +19 -16
- package/dist/apply--IVNSdXn.mjs.map +1 -0
- package/dist/astro/index.d.mts +9 -9
- package/dist/astro/index.mjs +3 -2
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +8 -8
- package/dist/astro/middleware/auth.mjs +2 -2
- package/dist/astro/middleware/redirect.mjs +4 -4
- package/dist/astro/middleware.d.mts +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +99 -70
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +5 -5
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +5 -5
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +2 -2
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +3 -3
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +3 -3
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +6 -6
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +6 -6
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +6 -6
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +12 -12
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +12 -12
- package/dist/astro/routes/api/admin/bylines/index.mjs +12 -12
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +10 -10
- package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -6
- package/dist/astro/routes/api/admin/comments/bulk.mjs +9 -9
- package/dist/astro/routes/api/admin/comments/counts.mjs +6 -6
- package/dist/astro/routes/api/admin/comments/index.mjs +9 -9
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +3 -3
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/index.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +32 -32
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +32 -32
- package/dist/astro/routes/api/admin/plugins/updates.mjs +31 -31
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +31 -31
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +31 -31
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +5 -5
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +2 -2
- package/dist/astro/routes/api/admin/users/index.mjs +5 -5
- package/dist/astro/routes/api/auth/dev-bypass.mjs +3 -3
- package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/complete.mjs +5 -5
- package/dist/astro/routes/api/auth/invite/index.mjs +5 -5
- package/dist/astro/routes/api/auth/invite/register-options.mjs +5 -5
- package/dist/astro/routes/api/auth/logout.mjs +2 -2
- package/dist/astro/routes/api/auth/magic-link/send.mjs +6 -6
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
- package/dist/astro/routes/api/auth/me.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +5 -5
- package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/options.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +5 -5
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +5 -5
- package/dist/astro/routes/api/auth/passkey/verify.mjs +5 -5
- package/dist/astro/routes/api/auth/signup/complete.mjs +5 -5
- package/dist/astro/routes/api/auth/signup/request.mjs +6 -6
- package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +9 -9
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +9 -9
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +11 -11
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/authors.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/index.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/trash.mjs +6 -6
- package/dist/astro/routes/api/dashboard.mjs +8 -8
- package/dist/astro/routes/api/dev/emails.mjs +2 -2
- package/dist/astro/routes/api/import/probe.d.mts +2 -2
- package/dist/astro/routes/api/import/probe.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +8 -8
- package/dist/astro/routes/api/import/wordpress/execute.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress/media.d.mts +13 -1
- package/dist/astro/routes/api/import/wordpress/media.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress/media.mjs +13 -9
- package/dist/astro/routes/api/import/wordpress/media.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +8 -8
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +8 -8
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +9 -9
- package/dist/astro/routes/api/manifest.mjs +3 -3
- package/dist/astro/routes/api/mcp.mjs +36 -36
- package/dist/astro/routes/api/media/_id_/confirm.d.mts.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +44 -8
- package/dist/astro/routes/api/media/_id_/confirm.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_.mjs +6 -6
- package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
- package/dist/astro/routes/api/media/providers/index.mjs +3 -3
- package/dist/astro/routes/api/media/upload-url.mjs +8 -8
- package/dist/astro/routes/api/media.mjs +22 -21
- package/dist/astro/routes/api/media.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_/items.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_/translations.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_.mjs +8 -8
- package/dist/astro/routes/api/menus/index.mjs +8 -8
- package/dist/astro/routes/api/oauth/device/authorize.mjs +3 -3
- package/dist/astro/routes/api/oauth/device/code.mjs +4 -4
- package/dist/astro/routes/api/oauth/device/token.mjs +4 -4
- package/dist/astro/routes/api/oauth/register.mjs +2 -2
- package/dist/astro/routes/api/oauth/token/refresh.mjs +3 -3
- package/dist/astro/routes/api/oauth/token/revoke.mjs +3 -3
- package/dist/astro/routes/api/oauth/token.mjs +2 -2
- package/dist/astro/routes/api/openapi.json.mjs +3 -3
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +3 -3
- package/dist/astro/routes/api/redirects/404s/index.mjs +8 -8
- package/dist/astro/routes/api/redirects/404s/summary.mjs +8 -8
- package/dist/astro/routes/api/redirects/_id_.mjs +9 -9
- package/dist/astro/routes/api/redirects/index.mjs +9 -9
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +31 -31
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +31 -31
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +31 -31
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +31 -31
- package/dist/astro/routes/api/schema/collections/index.mjs +31 -31
- package/dist/astro/routes/api/schema/index.mjs +6 -6
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +31 -31
- package/dist/astro/routes/api/schema/orphans/index.mjs +31 -31
- package/dist/astro/routes/api/search/enable.mjs +9 -9
- package/dist/astro/routes/api/search/index.mjs +8 -8
- package/dist/astro/routes/api/search/rebuild.mjs +9 -9
- package/dist/astro/routes/api/search/stats.mjs +6 -6
- package/dist/astro/routes/api/search/suggest.mjs +8 -8
- package/dist/astro/routes/api/sections/_slug_.mjs +8 -8
- package/dist/astro/routes/api/sections/index.mjs +8 -8
- package/dist/astro/routes/api/settings/email.mjs +4 -4
- package/dist/astro/routes/api/settings.mjs +11 -11
- package/dist/astro/routes/api/setup/admin-verify.mjs +5 -5
- package/dist/astro/routes/api/setup/admin.mjs +5 -5
- package/dist/astro/routes/api/setup/dev-bypass.mjs +17 -17
- package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
- package/dist/astro/routes/api/setup/index.mjs +20 -20
- package/dist/astro/routes/api/setup/status.mjs +3 -3
- package/dist/astro/routes/api/snapshot.mjs +3 -3
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +11 -11
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +11 -11
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +11 -11
- package/dist/astro/routes/api/taxonomies/index.mjs +11 -11
- package/dist/astro/routes/api/themes/preview.mjs +3 -3
- package/dist/astro/routes/api/typegen.mjs +5 -5
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +6 -6
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +8 -8
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +8 -8
- package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
- package/dist/astro/routes/api/widget-areas/index.mjs +8 -8
- package/dist/astro/routes/api/widget-components.mjs +2 -2
- package/dist/astro/routes/robots.txt.mjs +5 -5
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +11 -10
- package/dist/astro/routes/sitemap-_collection_.xml.mjs.map +1 -1
- package/dist/astro/routes/sitemap.xml.mjs +6 -6
- package/dist/astro/types.d.mts +10 -10
- package/dist/{authorize-B__u-iZJ.mjs → authorize-BZnOGJFo.mjs} +2 -2
- package/dist/{authorize-B__u-iZJ.mjs.map → authorize-BZnOGJFo.mjs.map} +1 -1
- package/dist/{byline-C-YajfV7.mjs → byline-LL8lyqdb.mjs} +6 -6
- package/dist/{byline-C-YajfV7.mjs.map → byline-LL8lyqdb.mjs.map} +1 -1
- package/dist/{byline-fields-Ca56gaT6.d.mts → byline-fields-KD3JFRuH.d.mts} +24 -6
- package/dist/byline-fields-KD3JFRuH.d.mts.map +1 -0
- package/dist/{byline-fields-B-KXkg5R.mjs → byline-fields-eyJMblSr.mjs} +2 -2
- package/dist/{byline-fields-B-KXkg5R.mjs.map → byline-fields-eyJMblSr.mjs.map} +1 -1
- package/dist/{bylines-D4KADDAi.mjs → bylines-BmJ7Vg7J.mjs} +4 -4
- package/dist/{bylines-D4KADDAi.mjs.map → bylines-BmJ7Vg7J.mjs.map} +1 -1
- package/dist/{bylines-BMtJnPnG.mjs → bylines-DE47vP8d.mjs} +6 -6
- package/dist/{bylines-BMtJnPnG.mjs.map → bylines-DE47vP8d.mjs.map} +1 -1
- package/dist/{cache-BUJe2w6Z.mjs → cache-D9jF5bYw.mjs} +2 -2
- package/dist/{cache-BUJe2w6Z.mjs.map → cache-D9jF5bYw.mjs.map} +1 -1
- package/dist/{chunks-BHgcHh2C.mjs → chunks-OCG163rN.mjs} +2 -2
- package/dist/{chunks-BHgcHh2C.mjs.map → chunks-OCG163rN.mjs.map} +1 -1
- package/dist/cli/index.mjs +17 -17
- package/dist/{comment-BDx50EX5.mjs → comment-D9BLDpZI.mjs} +3 -3
- package/dist/{comment-BDx50EX5.mjs.map → comment-D9BLDpZI.mjs.map} +1 -1
- package/dist/{comment-reaction-Gv_pgfI3.mjs → comment-reaction-Drl9SkNu.mjs} +2 -2
- package/dist/{comment-reaction-Gv_pgfI3.mjs.map → comment-reaction-Drl9SkNu.mjs.map} +1 -1
- package/dist/{comments-CXHuQ6jI.mjs → comments-BjJ9SKGU.mjs} +3 -3
- package/dist/{comments-CXHuQ6jI.mjs.map → comments-BjJ9SKGU.mjs.map} +1 -1
- package/dist/{content-PoVGKjp0.mjs → content-ncUWWjna.mjs} +10 -8
- package/dist/content-ncUWWjna.mjs.map +1 -0
- package/dist/{context-Dvws41_A.mjs → context-C_g-iJVA.mjs} +14 -8
- package/dist/context-C_g-iJVA.mjs.map +1 -0
- package/dist/{dashboard-DsYU8JKS.mjs → dashboard-CotyqQrW.mjs} +4 -4
- package/dist/{dashboard-DsYU8JKS.mjs.map → dashboard-CotyqQrW.mjs.map} +1 -1
- package/dist/db/index.d.mts +2 -2
- package/dist/db/index.mjs +1 -1
- package/dist/{db-errors-UXB1CK42.mjs → db-errors-CK46D-ly.mjs} +1 -1
- package/dist/{db-errors-UXB1CK42.mjs.map → db-errors-CK46D-ly.mjs.map} +1 -1
- package/dist/{placeholder-B0s2mWu5.mjs → enrich-BQ0mxJRs.mjs} +61 -10
- package/dist/enrich-BQ0mxJRs.mjs.map +1 -0
- package/dist/{error-lq_fL-RS.mjs → error-rVGiqWDt.mjs} +3 -2
- package/dist/error-rVGiqWDt.mjs.map +1 -0
- package/dist/{field-defs-cache-CYYx8xlG.mjs → field-defs-cache-ljXzJCfc.mjs} +2 -2
- package/dist/{field-defs-cache-CYYx8xlG.mjs.map → field-defs-cache-ljXzJCfc.mjs.map} +1 -1
- package/dist/{fts-manager-DW_NR-Fs.mjs → fts-manager-BbCCbEKA.mjs} +2 -2
- package/dist/{fts-manager-DW_NR-Fs.mjs.map → fts-manager-BbCCbEKA.mjs.map} +1 -1
- package/dist/{index-BroXT-vD.d.mts → index-BRNabKrx.d.mts} +53 -15
- package/dist/index-BRNabKrx.d.mts.map +1 -0
- package/dist/{index-qbL02uhs.d.mts → index-Dt0OKJB4.d.mts} +2 -2
- package/dist/{index-qbL02uhs.d.mts.map → index-Dt0OKJB4.d.mts.map} +1 -1
- package/dist/index.d.mts +12 -12
- package/dist/index.mjs +43 -43
- package/dist/{load-DMFi4n1p.mjs → load-DMjXghbZ.mjs} +2 -2
- package/dist/{load-DMFi4n1p.mjs.map → load-DMjXghbZ.mjs.map} +1 -1
- package/dist/{loader-BR2SgfEc.mjs → loader-BA_zhkS_.mjs} +82 -19
- package/dist/loader-BA_zhkS_.mjs.map +1 -0
- package/dist/{manifest-schema-BWKnQYQF.mjs → manifest-schema-kPGX7VS-.mjs} +4 -1
- package/dist/manifest-schema-kPGX7VS-.mjs.map +1 -0
- package/dist/media/image-endpoint.mjs +1 -1
- package/dist/media/index.d.mts +45 -2
- package/dist/media/index.d.mts.map +1 -1
- package/dist/media/index.mjs +5 -3
- package/dist/media/index.mjs.map +1 -1
- package/dist/media/local-runtime.d.mts +9 -9
- package/dist/media/local-runtime.d.mts.map +1 -1
- package/dist/media/local-runtime.mjs +17 -5
- package/dist/media/local-runtime.mjs.map +1 -1
- package/dist/{media-CTjJoJTh.mjs → media-C5t_eCnk.mjs} +4 -2
- package/dist/media-C5t_eCnk.mjs.map +1 -0
- package/dist/{media-allowlist-B77I5_bw.mjs → media-allowlist-Du8t1u6a.mjs} +2 -2
- package/dist/{media-allowlist-B77I5_bw.mjs.map → media-allowlist-Du8t1u6a.mjs.map} +1 -1
- package/dist/{menus-8O6xNcYW.mjs → menus-CBb6UrMU.mjs} +3 -3
- package/dist/{menus-8O6xNcYW.mjs.map → menus-CBb6UrMU.mjs.map} +1 -1
- package/dist/{menus-GvRSpgIA.mjs → menus-CYH09a41.mjs} +65 -41
- package/dist/menus-CYH09a41.mjs.map +1 -0
- package/dist/{mime-BTzvzJ8M.mjs → mime-CCEzze7W.mjs} +1 -1
- package/dist/{mime-BTzvzJ8M.mjs.map → mime-CCEzze7W.mjs.map} +1 -1
- package/dist/{normalize-ClbNIenm.mjs → normalize-CeglrBT9.mjs} +11 -2
- package/dist/normalize-CeglrBT9.mjs.map +1 -0
- package/dist/{object-cache-SEb2IM4Z.mjs → object-cache-CHbHv83-.mjs} +8 -4
- package/dist/object-cache-CHbHv83-.mjs.map +1 -0
- package/dist/{options-CqECG0gM.d.mts → options-41nCWqi9.d.mts} +3 -3
- package/dist/{options-CqECG0gM.d.mts.map → options-41nCWqi9.d.mts.map} +1 -1
- package/dist/page/index.d.mts +5 -2
- package/dist/page/index.d.mts.map +1 -1
- package/dist/page/index.mjs +3 -2
- package/dist/page/index.mjs.map +1 -1
- package/dist/{parse-DHI873qU.mjs → parse-BwAdS7FZ.mjs} +2 -2
- package/dist/{parse-DHI873qU.mjs.map → parse-BwAdS7FZ.mjs.map} +1 -1
- package/dist/{placeholder-QoI4gEzP.d.mts → placeholder-Cuce9U-m.d.mts} +20 -5
- package/dist/placeholder-Cuce9U-m.d.mts.map +1 -0
- package/dist/plugin-types.d.mts +5 -2
- package/dist/plugin-types.d.mts.map +1 -1
- package/dist/plugin-utils.d.mts +8 -8
- package/dist/plugins/adapt-sandbox-entry.d.mts +8 -8
- package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
- package/dist/{query-DZdlS6Cu.mjs → query-BpQb-blG.mjs} +15 -15
- package/dist/{query-DZdlS6Cu.mjs.map → query-BpQb-blG.mjs.map} +1 -1
- package/dist/{rate-limit-D1Y6VFVP.mjs → rate-limit-Dap6HMNM.mjs} +2 -2
- package/dist/{rate-limit-D1Y6VFVP.mjs.map → rate-limit-Dap6HMNM.mjs.map} +1 -1
- package/dist/{redirect-C1KVGYWe.mjs → redirect-_M0trWoo.mjs} +2 -2
- package/dist/{redirect-C1KVGYWe.mjs.map → redirect-_M0trWoo.mjs.map} +1 -1
- package/dist/{redirects-x9eFPeyi.mjs → redirects-Dcbuisoj.mjs} +2 -1
- package/dist/redirects-Dcbuisoj.mjs.map +1 -0
- package/dist/{redirects-DgnypVir.mjs → redirects-w0V9vj_j.mjs} +3 -3
- package/dist/{redirects-DgnypVir.mjs.map → redirects-w0V9vj_j.mjs.map} +1 -1
- package/dist/{registry-CJkCubOJ.mjs → registry-g8yYt4cC.mjs} +14 -4
- package/dist/registry-g8yYt4cC.mjs.map +1 -0
- package/dist/{runner-CEtq-6Sm.d.mts → runner-BbR3DfrL.d.mts} +2 -2
- package/dist/{runner-CEtq-6Sm.d.mts.map → runner-BbR3DfrL.d.mts.map} +1 -1
- package/dist/{runner-BsquExlK.mjs → runner-DPdt_Wpa.mjs} +250 -178
- package/dist/runner-DPdt_Wpa.mjs.map +1 -0
- package/dist/runtime.d.mts +9 -9
- package/dist/runtime.mjs +2 -2
- package/dist/{schema-DMjXuvNU.mjs → schema-DoLd3j2e.mjs} +5 -5
- package/dist/{schema-DMjXuvNU.mjs.map → schema-DoLd3j2e.mjs.map} +1 -1
- package/dist/{search-D1wKk3lc.mjs → search-BR0ZeZmh.mjs} +4 -4
- package/dist/{search-D1wKk3lc.mjs.map → search-BR0ZeZmh.mjs.map} +1 -1
- package/dist/{sections-CCMic8dB.mjs → sections-5J2YJPqJ.mjs} +3 -3
- package/dist/{sections-CCMic8dB.mjs.map → sections-5J2YJPqJ.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +15 -15
- package/dist/seo/index.d.mts +1 -1
- package/dist/{seo-DCEF-AVr.mjs → seo-BkhuuaaE.mjs} +25 -3
- package/dist/seo-BkhuuaaE.mjs.map +1 -0
- package/dist/{seo-3QrZ1EPT.mjs → seo-DKeXHkzE.mjs} +3 -3
- package/dist/{seo-3QrZ1EPT.mjs.map → seo-DKeXHkzE.mjs.map} +1 -1
- package/dist/{service-CQHK_10l.mjs → service-BbO2s8gT.mjs} +2 -2
- package/dist/{service-CQHK_10l.mjs.map → service-BbO2s8gT.mjs.map} +1 -1
- package/dist/{settings-C3Yi5Co3.mjs → settings-BMNr9Dmi.mjs} +3 -3
- package/dist/{settings-C3Yi5Co3.mjs.map → settings-BMNr9Dmi.mjs.map} +1 -1
- package/dist/{settings-D9rvaLuY.mjs → settings-DJeki-97.mjs} +4 -4
- package/dist/{settings-D9rvaLuY.mjs.map → settings-DJeki-97.mjs.map} +1 -1
- package/dist/{taxonomies-DhLaNJji.mjs → taxonomies-CM_fUqLU.mjs} +5 -4
- package/dist/taxonomies-CM_fUqLU.mjs.map +1 -0
- package/dist/{taxonomies-CurpLtNC.mjs → taxonomies-D13i2ctt.mjs} +76 -21
- package/dist/taxonomies-D13i2ctt.mjs.map +1 -0
- package/dist/{taxonomy-BBUlGug5.mjs → taxonomy-BU0LjdVb.mjs} +4 -4
- package/dist/{taxonomy-BBUlGug5.mjs.map → taxonomy-BU0LjdVb.mjs.map} +1 -1
- package/dist/{types-TTx8uP7V.d.mts → types-BYnDBVwe.d.mts} +27 -5
- package/dist/{types-TTx8uP7V.d.mts.map → types-BYnDBVwe.d.mts.map} +1 -1
- package/dist/{types-CTLJYMhN.d.mts → types-CNlaBFzx.d.mts} +11 -1
- package/dist/{types-CTLJYMhN.d.mts.map → types-CNlaBFzx.d.mts.map} +1 -1
- package/dist/{types-CmI1OyQg.mjs → types-CZgK3EMW.mjs} +2 -2
- package/dist/{types-CmI1OyQg.mjs.map → types-CZgK3EMW.mjs.map} +1 -1
- package/dist/{types-BWh2MjmA.d.mts → types-ETmO_jQr.d.mts} +2 -2
- package/dist/{types-BWh2MjmA.d.mts.map → types-ETmO_jQr.d.mts.map} +1 -1
- package/dist/types-tM44hEcf.mjs.map +1 -1
- package/dist/{types-B-gIxFCE.d.mts → types-xIfVRNLp.d.mts} +38 -1
- package/dist/types-xIfVRNLp.d.mts.map +1 -0
- package/dist/{user-JAuc8VUf.mjs → user-DkG2jKCp.mjs} +3 -3
- package/dist/{user-JAuc8VUf.mjs.map → user-DkG2jKCp.mjs.map} +1 -1
- package/dist/{validate-DOFrIb-c.d.mts → validate-CG0V0wNR.d.mts} +3 -3
- package/dist/{validate-DOFrIb-c.d.mts.map → validate-CG0V0wNR.d.mts.map} +1 -1
- package/dist/{validate-BzXHWnkV.mjs → validate-CuJ1Ps9j.mjs} +2 -2
- package/dist/{validate-BzXHWnkV.mjs.map → validate-CuJ1Ps9j.mjs.map} +1 -1
- package/dist/{validation-CY1gk4Ar.mjs → validation-CwUfTdGF.mjs} +6 -6
- package/dist/{validation-CY1gk4Ar.mjs.map → validation-CwUfTdGF.mjs.map} +1 -1
- package/dist/version-BoNBItP_.mjs +7 -0
- package/dist/{version-C0JPFUG8.mjs.map → version-BoNBItP_.mjs.map} +1 -1
- package/dist/{widgets-Dri0spI4.mjs → widgets-BfOZ-ajY.mjs} +2 -2
- package/dist/{widgets-Dri0spI4.mjs.map → widgets-BfOZ-ajY.mjs.map} +1 -1
- package/dist/{zod-generator-xEHODMJf.mjs → zod-generator-gFsRuNNy.mjs} +6 -3
- package/dist/zod-generator-gFsRuNNy.mjs.map +1 -0
- package/package.json +6 -6
- package/src/api/errors.ts +1 -0
- package/src/api/handlers/content.ts +4 -4
- package/src/api/handlers/seo.ts +30 -2
- package/src/api/handlers/taxonomies.ts +5 -1
- package/src/api/schemas/schema.ts +1 -0
- package/src/astro/integration/virtual-modules.ts +15 -0
- package/src/astro/middleware.ts +14 -1
- package/src/astro/routes/api/import/wordpress/media.ts +9 -3
- package/src/astro/routes/api/media/[id]/confirm.ts +62 -2
- package/src/astro/routes/api/media.ts +17 -19
- package/src/astro/routes/sitemap-[collection].xml.ts +5 -4
- package/src/components/EmDashHead.astro +23 -3
- package/src/components/EmDashImage.astro +4 -7
- package/src/components/Image.astro +14 -4
- package/src/components/InlinePortableTextEditor.tsx +48 -1
- package/src/components/JsonLdScript.astro +9 -0
- package/src/database/migrations/036_i18n_menus_and_taxonomies.ts +16 -0
- package/src/database/migrations/046_media_usage_index.ts +115 -0
- package/src/database/migrations/047_restore_taxonomy_parent_index.ts +29 -0
- package/src/database/migrations/runner.ts +4 -0
- package/src/database/repositories/content.ts +8 -5
- package/src/database/repositories/media-usage.ts +444 -0
- package/src/database/repositories/media.ts +9 -1
- package/src/database/types.ts +39 -0
- package/src/emdash-runtime.ts +72 -25
- package/src/index.ts +3 -0
- package/src/loader.ts +110 -31
- package/src/media/enrich.ts +78 -0
- package/src/media/index.ts +1 -0
- package/src/media/local-runtime.ts +19 -0
- package/src/media/normalize.ts +20 -1
- package/src/media/placeholder.ts +29 -9
- package/src/media/types.ts +14 -0
- package/src/media/usage/extractor.ts +302 -0
- package/src/media/usage/types.ts +46 -0
- package/src/object-cache/index.ts +8 -2
- package/src/page/metadata.ts +23 -3
- package/src/plugin-types.ts +12 -0
- package/src/plugins/context.ts +8 -0
- package/src/plugins/define-plugin.ts +14 -5
- package/src/plugins/hooks.ts +66 -33
- package/src/plugins/index.ts +6 -0
- package/src/plugins/manager.ts +33 -0
- package/src/plugins/manifest-schema.ts +3 -0
- package/src/plugins/types.ts +41 -2
- package/src/schema/registry.ts +25 -0
- package/src/schema/types.ts +10 -0
- package/src/schema/zod-generator.ts +9 -1
- package/src/seed/apply.ts +7 -0
- package/src/taxonomies/index.ts +121 -18
- package/dist/api-d_0wi9fW.mjs.map +0 -1
- package/dist/apply-BE9UaWX0.mjs.map +0 -1
- package/dist/byline-fields-Ca56gaT6.d.mts.map +0 -1
- package/dist/content-PoVGKjp0.mjs.map +0 -1
- package/dist/context-Dvws41_A.mjs.map +0 -1
- package/dist/error-lq_fL-RS.mjs.map +0 -1
- package/dist/index-BroXT-vD.d.mts.map +0 -1
- package/dist/loader-BR2SgfEc.mjs.map +0 -1
- package/dist/manifest-schema-BWKnQYQF.mjs.map +0 -1
- package/dist/media-CTjJoJTh.mjs.map +0 -1
- package/dist/menus-GvRSpgIA.mjs.map +0 -1
- package/dist/normalize-ClbNIenm.mjs.map +0 -1
- package/dist/object-cache-SEb2IM4Z.mjs.map +0 -1
- package/dist/placeholder-B0s2mWu5.mjs.map +0 -1
- package/dist/placeholder-QoI4gEzP.d.mts.map +0 -1
- package/dist/redirects-x9eFPeyi.mjs.map +0 -1
- package/dist/registry-CJkCubOJ.mjs.map +0 -1
- package/dist/runner-BsquExlK.mjs.map +0 -1
- package/dist/seo-DCEF-AVr.mjs.map +0 -1
- package/dist/taxonomies-CurpLtNC.mjs.map +0 -1
- package/dist/taxonomies-DhLaNJji.mjs.map +0 -1
- package/dist/types-B-gIxFCE.d.mts.map +0 -1
- package/dist/version-C0JPFUG8.mjs +0 -7
- package/dist/zod-generator-xEHODMJf.mjs.map +0 -1
package/src/api/handlers/seo.ts
CHANGED
|
@@ -56,6 +56,34 @@ export interface SitemapDataResponse {
|
|
|
56
56
|
/** Maximum entries per sitemap (per spec) */
|
|
57
57
|
const SITEMAP_MAX_ENTRIES = 50_000;
|
|
58
58
|
|
|
59
|
+
/** Matches a trailing timezone designator (`Z` or `±HH`, `±HHMM`, `±HH:MM`). */
|
|
60
|
+
const TZ_SUFFIX_RE = /([zZ]|[+-]\d{2}(:?\d{2})?)$/;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Normalize a stored timestamp to W3C Datetime (ISO 8601) for sitemaps.
|
|
64
|
+
*
|
|
65
|
+
* `updated_at` is not guaranteed to be ISO: the column default is
|
|
66
|
+
* `datetime('now')` on SQLite and `CURRENT_TIMESTAMP` on Postgres, both of
|
|
67
|
+
* which store a space-separated `YYYY-MM-DD HH:MM:SS` string (and imported
|
|
68
|
+
* content can carry other shapes). The sitemap `<lastmod>` field requires
|
|
69
|
+
* W3C Datetime, and Google Search Console rejects the space-separated form
|
|
70
|
+
* as "Invalid date". Normalize defensively, assuming UTC when no offset is
|
|
71
|
+
* present (matches SQLite's `datetime('now')`). Valid date strings are
|
|
72
|
+
* normalized to UTC ISO 8601; unparseable values are returned as-is.
|
|
73
|
+
*/
|
|
74
|
+
function toW3CDate(value: string): string {
|
|
75
|
+
if (!value) return value;
|
|
76
|
+
let normalized = value.trim();
|
|
77
|
+
if (normalized.includes(" ") && !normalized.includes("T")) {
|
|
78
|
+
normalized = normalized.replace(" ", "T");
|
|
79
|
+
}
|
|
80
|
+
if (!TZ_SUFFIX_RE.test(normalized)) {
|
|
81
|
+
normalized += "Z";
|
|
82
|
+
}
|
|
83
|
+
const parsed = Date.parse(normalized);
|
|
84
|
+
return Number.isNaN(parsed) ? value : new Date(parsed).toISOString();
|
|
85
|
+
}
|
|
86
|
+
|
|
59
87
|
/**
|
|
60
88
|
* Collect all published, indexable content across SEO-enabled collections
|
|
61
89
|
* for sitemap generation, grouped by collection.
|
|
@@ -133,7 +161,7 @@ export async function handleSitemapData(
|
|
|
133
161
|
entries.push({
|
|
134
162
|
id: row.id,
|
|
135
163
|
slug: row.slug,
|
|
136
|
-
updatedAt: row.updated_at,
|
|
164
|
+
updatedAt: toW3CDate(row.updated_at),
|
|
137
165
|
locale: row.locale,
|
|
138
166
|
translationGroup: row.translation_group,
|
|
139
167
|
image: row.seo_image ?? null,
|
|
@@ -144,7 +172,7 @@ export async function handleSitemapData(
|
|
|
144
172
|
collection: col.slug,
|
|
145
173
|
urlPattern: col.url_pattern,
|
|
146
174
|
// Rows are ordered by updated_at DESC, so first row is the latest
|
|
147
|
-
lastmod: rows.rows[0].updated_at,
|
|
175
|
+
lastmod: toW3CDate(rows.rows[0].updated_at),
|
|
148
176
|
entries,
|
|
149
177
|
});
|
|
150
178
|
} catch (err) {
|
|
@@ -12,7 +12,7 @@ import { ulid } from "ulidx";
|
|
|
12
12
|
|
|
13
13
|
import { TaxonomyRepository } from "../../database/repositories/taxonomy.js";
|
|
14
14
|
import type { Database, TaxonomyDefTable } from "../../database/types.js";
|
|
15
|
-
import { invalidateTermCache } from "../../taxonomies/index.js";
|
|
15
|
+
import { invalidateTaxonomyDefsCache, invalidateTermCache } from "../../taxonomies/index.js";
|
|
16
16
|
import type { ApiResult } from "../types.js";
|
|
17
17
|
|
|
18
18
|
const NAME_PATTERN = /^[a-z][a-z0-9_]*$/;
|
|
@@ -290,6 +290,10 @@ export async function handleTaxonomyCreate(
|
|
|
290
290
|
})
|
|
291
291
|
.execute();
|
|
292
292
|
|
|
293
|
+
// A new def changes which taxonomies exist — drop the isolate-wide
|
|
294
|
+
// defs/names caches so this isolate reflects it immediately.
|
|
295
|
+
invalidateTaxonomyDefsCache();
|
|
296
|
+
|
|
293
297
|
const row = await db
|
|
294
298
|
.selectFrom("_emdash_taxonomy_defs")
|
|
295
299
|
.selectAll()
|
|
@@ -125,6 +125,7 @@ export const createFieldBody = z
|
|
|
125
125
|
export const updateFieldBody = z
|
|
126
126
|
.object({
|
|
127
127
|
label: z.string().min(1).optional(),
|
|
128
|
+
type: fieldTypeValues.optional(),
|
|
128
129
|
required: z.boolean().optional(),
|
|
129
130
|
unique: z.boolean().optional(),
|
|
130
131
|
defaultValue: z.unknown().optional(),
|
|
@@ -242,6 +242,21 @@ export function generatePluginsModule(descriptors: PluginDescriptor[]): string {
|
|
|
242
242
|
let needsAdapter = false;
|
|
243
243
|
|
|
244
244
|
descriptors.forEach((descriptor, index) => {
|
|
245
|
+
// Every `plugins: []` entry must resolve to a file/package entrypoint that
|
|
246
|
+
// can be statically imported and bundled at build time. An in-process
|
|
247
|
+
// `definePlugin({...})` result passed directly has no entrypoint; without
|
|
248
|
+
// this guard the generator emitted `import pluginDefN from "undefined";`,
|
|
249
|
+
// which failed deep in Rollup with `failed to resolve import "undefined"`
|
|
250
|
+
// (#1416). Fail fast with an actionable message instead.
|
|
251
|
+
if (!descriptor.entrypoint) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
`[emdash] Plugin "${descriptor.id}" has no \`entrypoint\`. The astro integration's ` +
|
|
254
|
+
`\`plugins: []\` requires plugins that resolve to a file/package entrypoint so they can be ` +
|
|
255
|
+
`bundled at build time; an in-process \`definePlugin({...})\` result passed directly is not ` +
|
|
256
|
+
`supported. Move the plugin into its own module and reference it via a factory that returns ` +
|
|
257
|
+
`a descriptor with an \`entrypoint\` (e.g. \`plugins: [myPlugin()]\`).`,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
245
260
|
if (descriptor.format === "standard") {
|
|
246
261
|
// Standard format: import default export, wrap with adaptSandboxEntry
|
|
247
262
|
needsAdapter = true;
|
package/src/astro/middleware.ts
CHANGED
|
@@ -490,6 +490,18 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
490
490
|
const sessionUser =
|
|
491
491
|
context.isPrerendered || !hasSessionCookie ? null : await resolveSessionUser(context.session);
|
|
492
492
|
|
|
493
|
+
// Credentialed API requests (API tokens `ec_pat_*`, OAuth tokens
|
|
494
|
+
// `ec_oat_*`, and other Bearer credentials) carry no `astro-session`
|
|
495
|
+
// cookie, so `sessionUser` is null for them -- yet they still expect
|
|
496
|
+
// read-your-writes. The auth middleware that resolves the token runs
|
|
497
|
+
// *after* this one, so `locals.user` isn't populated here; detect the
|
|
498
|
+
// credential directly on the request. Request-scoped adapters use this to
|
|
499
|
+
// keep such requests on the primary/uncached connection (not a lagging
|
|
500
|
+
// read replica or the Hyperdrive query cache).
|
|
501
|
+
const hasBearerAuth = (request.headers.get("authorization") ?? "")
|
|
502
|
+
.toLowerCase()
|
|
503
|
+
.startsWith("bearer ");
|
|
504
|
+
|
|
493
505
|
if (!isEmDashRoute && !isPublicRuntimeRoute && !hasEditCookie && !hasPreviewToken) {
|
|
494
506
|
if (!sessionUser && !playgroundDb) {
|
|
495
507
|
const timings: Array<{ name: string; dur: number; desc?: string }> = [];
|
|
@@ -668,6 +680,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
668
680
|
// Content handlers
|
|
669
681
|
handleContentList: runtime.handleContentList.bind(runtime),
|
|
670
682
|
handleContentGet: runtime.handleContentGet.bind(runtime),
|
|
683
|
+
handleContentAuthors: runtime.handleContentAuthors.bind(runtime),
|
|
671
684
|
handleContentCreate: runtime.handleContentCreate.bind(runtime),
|
|
672
685
|
handleContentUpdate: runtime.handleContentUpdate.bind(runtime),
|
|
673
686
|
handleContentDelete: runtime.handleContentDelete.bind(runtime),
|
|
@@ -775,7 +788,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
775
788
|
// per-request state (e.g. a D1 bookmark cookie for read-your-writes).
|
|
776
789
|
const scoped = createRequestScopedDb({
|
|
777
790
|
config: config?.database?.config,
|
|
778
|
-
isAuthenticated: !!sessionUser,
|
|
791
|
+
isAuthenticated: !!sessionUser || hasBearerAuth,
|
|
779
792
|
isWrite: request.method !== "GET" && request.method !== "HEAD",
|
|
780
793
|
cookies: context.cookies,
|
|
781
794
|
url,
|
|
@@ -20,6 +20,7 @@ import { apiError, apiSuccess, handleError } from "#api/error.js";
|
|
|
20
20
|
import { isParseError, parseBody } from "#api/parse.js";
|
|
21
21
|
import { wpMediaImportBody } from "#api/schemas.js";
|
|
22
22
|
import { validateExternalUrl, ssrfSafeFetch, SsrfError } from "#import/ssrf.js";
|
|
23
|
+
import { enrichImageMetadata } from "#media/enrich.js";
|
|
23
24
|
import type { EmDashHandlers } from "#types";
|
|
24
25
|
|
|
25
26
|
import type { AttachmentInfo } from "./analyze.js";
|
|
@@ -125,7 +126,7 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
|
|
125
126
|
}
|
|
126
127
|
};
|
|
127
128
|
|
|
128
|
-
async function importMediaWithProgress(
|
|
129
|
+
export async function importMediaWithProgress(
|
|
129
130
|
attachments: AttachmentInfo[],
|
|
130
131
|
db: NonNullable<EmDashHandlers["db"]>,
|
|
131
132
|
storage: NonNullable<EmDashHandlers["storage"]>,
|
|
@@ -273,6 +274,9 @@ async function importMediaWithProgress(
|
|
|
273
274
|
contentType,
|
|
274
275
|
});
|
|
275
276
|
|
|
277
|
+
// Derive dimensions + LQIP placeholders (no-op for non-images).
|
|
278
|
+
const enriched = await enrichImageMetadata(new Uint8Array(buffer), contentType);
|
|
279
|
+
|
|
276
280
|
// Create media record with content hash
|
|
277
281
|
const mediaItem = await repo.create({
|
|
278
282
|
filename: attachment.filename || `media-${attachment.id}${ext}`,
|
|
@@ -280,8 +284,10 @@ async function importMediaWithProgress(
|
|
|
280
284
|
size,
|
|
281
285
|
storageKey,
|
|
282
286
|
contentHash,
|
|
283
|
-
width:
|
|
284
|
-
height:
|
|
287
|
+
width: enriched.width,
|
|
288
|
+
height: enriched.height,
|
|
289
|
+
blurhash: enriched.blurhash,
|
|
290
|
+
dominantColor: enriched.dominantColor,
|
|
285
291
|
});
|
|
286
292
|
|
|
287
293
|
// Build the new URL
|
|
@@ -14,10 +14,20 @@ import { requireOwnerPerm, requirePerm } from "#api/authorize.js";
|
|
|
14
14
|
import { apiError, apiSuccess, handleError } from "#api/error.js";
|
|
15
15
|
import { isParseError, parseOptionalBody } from "#api/parse.js";
|
|
16
16
|
import { mediaConfirmBody } from "#api/schemas.js";
|
|
17
|
+
import { enrichImageMetadata } from "#media/enrich.js";
|
|
17
18
|
import type { MediaItem } from "#types";
|
|
18
19
|
|
|
19
20
|
export const prerender = false;
|
|
20
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Max raw bytes to buffer for server-side LQIP generation at confirm time. The
|
|
24
|
+
* signed-URL upload flow exists so large files bypass server buffering — re-reading
|
|
25
|
+
* the whole object into a Worker's 128 MB heap to compute a blurhash would OOM
|
|
26
|
+
* on the very uploads that flow was designed for. LQIP is progressive
|
|
27
|
+
* enhancement: large images simply ship without a server-generated placeholder.
|
|
28
|
+
*/
|
|
29
|
+
const MAX_PLACEHOLDER_DOWNLOAD_BYTES = 8 * 1024 * 1024;
|
|
30
|
+
|
|
21
31
|
/**
|
|
22
32
|
* Add URL to media item (relative URL for portability)
|
|
23
33
|
*/
|
|
@@ -81,11 +91,61 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
|
|
|
81
91
|
}
|
|
82
92
|
}
|
|
83
93
|
|
|
94
|
+
// For images, read the just-uploaded bytes back from storage once to
|
|
95
|
+
// generate LQIP placeholders (and server-side dimensions as a fallback).
|
|
96
|
+
// The signed-URL flow uploads directly to storage, so this confirm is the
|
|
97
|
+
// only point at which the server sees the bytes. Best-effort: a decode
|
|
98
|
+
// failure must not block the upload from being marked ready. We also cap
|
|
99
|
+
// the download size — buffering a large original into a Worker heap to
|
|
100
|
+
// compute a 32px blurhash would OOM on the uploads the signed-URL path
|
|
101
|
+
// exists to support, so oversized files skip the server-side placeholder.
|
|
102
|
+
let blurhash: string | undefined;
|
|
103
|
+
let dominantColor: string | undefined;
|
|
104
|
+
let width = body.width;
|
|
105
|
+
let height = body.height;
|
|
106
|
+
if (emdash.storage && existing.mimeType.startsWith("image/")) {
|
|
107
|
+
const knownSize = body.size ?? existing.size ?? undefined;
|
|
108
|
+
const tooLarge = knownSize != null && knownSize > MAX_PLACEHOLDER_DOWNLOAD_BYTES;
|
|
109
|
+
if (!tooLarge) {
|
|
110
|
+
try {
|
|
111
|
+
const { body: stream } = await emdash.storage.download(existing.storageKey);
|
|
112
|
+
const bytes = new Uint8Array(await new Response(stream).arrayBuffer());
|
|
113
|
+
// Defense-in-depth for the unknown-size case: even though we
|
|
114
|
+
// already buffered it, refuse the decode so we don't also pay
|
|
115
|
+
// the (larger) RGBA allocation.
|
|
116
|
+
if (bytes.byteLength > MAX_PLACEHOLDER_DOWNLOAD_BYTES) {
|
|
117
|
+
console.warn(
|
|
118
|
+
`[media] confirm skipping placeholder: object ${existing.storageKey} is ${bytes.byteLength} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`,
|
|
119
|
+
);
|
|
120
|
+
} else {
|
|
121
|
+
const enriched = await enrichImageMetadata(bytes, existing.mimeType, {
|
|
122
|
+
knownDimensions:
|
|
123
|
+
body.width != null && body.height != null
|
|
124
|
+
? { width: body.width, height: body.height }
|
|
125
|
+
: undefined,
|
|
126
|
+
});
|
|
127
|
+
blurhash = enriched.blurhash;
|
|
128
|
+
dominantColor = enriched.dominantColor;
|
|
129
|
+
width = width ?? enriched.width;
|
|
130
|
+
height = height ?? enriched.height;
|
|
131
|
+
}
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error("[media] confirm placeholder generation failed:", error);
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
console.warn(
|
|
137
|
+
`[media] confirm skipping placeholder: object ${existing.storageKey} reported size ${knownSize} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
84
142
|
// Confirm the upload
|
|
85
143
|
const item = await repo.confirmUpload(id, {
|
|
86
144
|
size: body.size,
|
|
87
|
-
width
|
|
88
|
-
height
|
|
145
|
+
width,
|
|
146
|
+
height,
|
|
147
|
+
blurhash,
|
|
148
|
+
dominantColor,
|
|
89
149
|
});
|
|
90
150
|
|
|
91
151
|
if (!item) {
|
|
@@ -16,8 +16,8 @@ import { GLOBAL_UPLOAD_ALLOWLIST, resolveFieldAllowlist } from "#api/handlers/me
|
|
|
16
16
|
import { isParseError, parseQuery } from "#api/parse.js";
|
|
17
17
|
import { DEFAULT_MAX_UPLOAD_SIZE, formatFileSize, mediaListQuery } from "#api/schemas.js";
|
|
18
18
|
import { MediaRepository } from "#db/repositories/media.js";
|
|
19
|
+
import { enrichImageMetadata } from "#media/enrich.js";
|
|
19
20
|
import { matchesMimeAllowlist, normalizeMime } from "#media/mime.js";
|
|
20
|
-
import { generatePlaceholder } from "#media/placeholder.js";
|
|
21
21
|
import { computeContentHash } from "#utils/hash.js";
|
|
22
22
|
|
|
23
23
|
import type { MediaItem } from "../../types.js";
|
|
@@ -163,34 +163,32 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
|
|
163
163
|
const width = widthStr ? parseInt(widthStr, 10) : undefined;
|
|
164
164
|
const height = heightStr ? parseInt(heightStr, 10) : undefined;
|
|
165
165
|
|
|
166
|
-
//
|
|
167
|
-
// If the client sent a thumbnail
|
|
168
|
-
//
|
|
166
|
+
// Derive dimensions + LQIP placeholders via the shared helper.
|
|
167
|
+
// If the client sent a downscaled thumbnail, decode that for the blurhash
|
|
168
|
+
// (avoids OOM on large originals on memory-constrained runtimes).
|
|
169
169
|
const thumbnailEntry = formData.get("thumbnail");
|
|
170
170
|
const thumbnail = thumbnailEntry instanceof File ? thumbnailEntry : null;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
} else {
|
|
178
|
-
const clientDims = width && height ? { width, height } : undefined;
|
|
179
|
-
placeholder = await generatePlaceholder(buffer, file.type, clientDims);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
171
|
+
const enriched = await enrichImageMetadata(buffer, file.type, {
|
|
172
|
+
knownDimensions: width != null && height != null ? { width, height } : undefined,
|
|
173
|
+
placeholder: thumbnail
|
|
174
|
+
? { bytes: new Uint8Array(await thumbnail.arrayBuffer()), contentType: thumbnail.type }
|
|
175
|
+
: undefined,
|
|
176
|
+
});
|
|
182
177
|
|
|
183
178
|
// Create media record
|
|
184
179
|
const result = await emdash.handleMediaCreate({
|
|
185
180
|
filename: file.name,
|
|
186
181
|
mimeType: normalizeMime(file.type),
|
|
187
182
|
size: file.size,
|
|
188
|
-
|
|
189
|
-
|
|
183
|
+
// Client dimensions win over server header dimensions: the browser's
|
|
184
|
+
// naturalWidth/Height apply EXIF orientation, while image-size reports
|
|
185
|
+
// raw (pre-orientation) header dims — swapped for 90°/270° JPEGs.
|
|
186
|
+
width: width ?? enriched.width,
|
|
187
|
+
height: height ?? enriched.height,
|
|
190
188
|
storageKey,
|
|
191
189
|
contentHash,
|
|
192
|
-
blurhash:
|
|
193
|
-
dominantColor:
|
|
190
|
+
blurhash: enriched.blurhash,
|
|
191
|
+
dominantColor: enriched.dominantColor,
|
|
194
192
|
authorId: user?.id,
|
|
195
193
|
});
|
|
196
194
|
|
|
@@ -138,11 +138,12 @@ export const GET: APIRoute = async ({ params, locals, url }) => {
|
|
|
138
138
|
lines.push(" </image:image>");
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
const alternateEntries = siblings ?? (useXhtml ? [entry] : null);
|
|
142
|
+
if (useXhtml && alternateEntries) {
|
|
142
143
|
// Emit one xhtml:link per sibling (including self -- Google
|
|
143
144
|
// recommends including the page's own hreflang annotation).
|
|
144
145
|
// Siblings with unroutable locales are skipped here too.
|
|
145
|
-
for (const sib of
|
|
146
|
+
for (const sib of alternateEntries) {
|
|
146
147
|
const sibLoc = await resolveEntryUrl(sib);
|
|
147
148
|
if (sibLoc === null) continue;
|
|
148
149
|
lines.push(
|
|
@@ -154,13 +155,13 @@ export const GET: APIRoute = async ({ params, locals, url }) => {
|
|
|
154
155
|
// the first sibling with a routable URL. Stable order:
|
|
155
156
|
// rows arrive sorted by updated_at DESC from the handler.
|
|
156
157
|
const defaultSibling =
|
|
157
|
-
i18nConfig &&
|
|
158
|
+
i18nConfig && alternateEntries.find((s) => s.locale === i18nConfig.defaultLocale);
|
|
158
159
|
let xDefaultLoc: string | null = null;
|
|
159
160
|
if (defaultSibling) {
|
|
160
161
|
xDefaultLoc = await resolveEntryUrl(defaultSibling);
|
|
161
162
|
}
|
|
162
163
|
if (xDefaultLoc === null) {
|
|
163
|
-
for (const sib of
|
|
164
|
+
for (const sib of alternateEntries) {
|
|
164
165
|
const sibLoc = await resolveEntryUrl(sib);
|
|
165
166
|
if (sibLoc !== null) {
|
|
166
167
|
xDefaultLoc = sibLoc;
|
|
@@ -17,8 +17,14 @@
|
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
19
|
import type { PublicPageContext, PageMetadataContribution } from "../plugins/types.js";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
createSha256CspHash,
|
|
22
|
+
resolvePageMetadata,
|
|
23
|
+
renderPageMetadata,
|
|
24
|
+
type ResolvedPageMetadata,
|
|
25
|
+
} from "../page/metadata.js";
|
|
21
26
|
import { renderFragments } from "../page/fragments.js";
|
|
27
|
+
import JsonLdScript from "./JsonLdScript.astro";
|
|
22
28
|
import {
|
|
23
29
|
generateBaseSeoContributions,
|
|
24
30
|
generateSiteSeoContributions,
|
|
@@ -38,6 +44,7 @@ const runtime = getPageRuntime(Astro.locals as Record<string, unknown>);
|
|
|
38
44
|
let metadataHtml = "";
|
|
39
45
|
let siteIdentityHtml = "";
|
|
40
46
|
let fragmentsHtml = "";
|
|
47
|
+
let jsonLdScripts: ResolvedPageMetadata["jsonld"] = [];
|
|
41
48
|
|
|
42
49
|
if (runtime) {
|
|
43
50
|
// Run independent async loads in parallel: site settings (SEO meta
|
|
@@ -79,17 +86,30 @@ if (runtime) {
|
|
|
79
86
|
const siteContributions = generateSiteSeoContributions(siteSettings.seo);
|
|
80
87
|
const allContributions = [...pluginContributions, ...siteContributions, ...baseContributions];
|
|
81
88
|
const resolved = resolvePageMetadata(allContributions);
|
|
82
|
-
|
|
89
|
+
jsonLdScripts = resolved.jsonld;
|
|
90
|
+
metadataHtml = renderPageMetadata(resolved, { includeJsonLd: false });
|
|
83
91
|
siteIdentityHtml = renderSiteIdentity({ favicon: siteSettings.favicon });
|
|
84
92
|
fragmentsHtml = renderFragments(fragments, "head");
|
|
85
93
|
} else {
|
|
86
94
|
// No runtime (EmDash not initialized) — still render base SEO
|
|
87
95
|
const baseContributions: PageMetadataContribution[] = generateBaseSeoContributions(page);
|
|
88
96
|
const resolved = resolvePageMetadata(baseContributions);
|
|
89
|
-
|
|
97
|
+
jsonLdScripts = resolved.jsonld;
|
|
98
|
+
metadataHtml = renderPageMetadata(resolved, { includeJsonLd: false });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (Astro.csp && jsonLdScripts.length > 0) {
|
|
102
|
+
await Promise.all(
|
|
103
|
+
jsonLdScripts.map(async ({ json }) => {
|
|
104
|
+
Astro.csp?.insertScriptHash(await createSha256CspHash(json));
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
90
107
|
}
|
|
91
108
|
---
|
|
92
109
|
|
|
93
110
|
<Fragment set:html={metadataHtml} />
|
|
111
|
+
{
|
|
112
|
+
jsonLdScripts.map(({ json }) => <JsonLdScript json={json} />)
|
|
113
|
+
}
|
|
94
114
|
<Fragment set:html={siteIdentityHtml} />
|
|
95
115
|
<Fragment set:html={fragmentsHtml} />
|
|
@@ -158,15 +158,12 @@ if (img) {
|
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
// Build placeholder background style
|
|
161
|
+
// Build placeholder background style. Prefer the first-class MediaValue fields;
|
|
162
|
+
// fall back to `meta` for snapshots stored before LQIP was promoted off `meta`.
|
|
162
163
|
const blurhash =
|
|
163
|
-
|
|
164
|
-
? (image?.meta?.blurhash as string | undefined)
|
|
165
|
-
: undefined;
|
|
164
|
+
img?.blurhash ?? (img?.meta?.blurhash as string | undefined);
|
|
166
165
|
const dominantColor =
|
|
167
|
-
|
|
168
|
-
? (image?.meta?.dominantColor as string | undefined)
|
|
169
|
-
: undefined;
|
|
166
|
+
img?.dominantColor ?? (img?.meta?.dominantColor as string | undefined);
|
|
170
167
|
|
|
171
168
|
let placeholderStyle = "";
|
|
172
169
|
if (blurhash) {
|
|
@@ -25,7 +25,8 @@ export interface Props {
|
|
|
25
25
|
url?: string;
|
|
26
26
|
/** Provider ID for external media (e.g., "cloudflare-images") */
|
|
27
27
|
provider?: string;
|
|
28
|
-
/** Provider metadata
|
|
28
|
+
/** Provider metadata. LQIP is read from first-class fields below;
|
|
29
|
+
* `meta` only remains as a fallback for legacy snapshots. */
|
|
29
30
|
meta?: Record<string, unknown>;
|
|
30
31
|
};
|
|
31
32
|
alt?: string;
|
|
@@ -34,6 +35,10 @@ export interface Props {
|
|
|
34
35
|
width?: number;
|
|
35
36
|
/** Original image height */
|
|
36
37
|
height?: number;
|
|
38
|
+
/** LQIP blurhash — first-class field (legacy snapshots store it in `asset.meta`). */
|
|
39
|
+
blurhash?: string;
|
|
40
|
+
/** LQIP dominant color — first-class field (legacy snapshots store it in `asset.meta`). */
|
|
41
|
+
dominantColor?: string;
|
|
37
42
|
/** Display width for this instance (overrides original) */
|
|
38
43
|
displayWidth?: number;
|
|
39
44
|
/** Display height for this instance (overrides original) */
|
|
@@ -152,9 +157,14 @@ if (!src) {
|
|
|
152
157
|
}
|
|
153
158
|
}
|
|
154
159
|
|
|
155
|
-
// Build placeholder background style
|
|
156
|
-
|
|
157
|
-
const
|
|
160
|
+
// Build placeholder background style. Prefer first-class LQIP fields; fall
|
|
161
|
+
// back to `asset.meta` for legacy snapshots persisted before LQIP was promoted.
|
|
162
|
+
const blurhash = (node.blurhash ?? (asset.meta?.blurhash as string | undefined)) as
|
|
163
|
+
| string
|
|
164
|
+
| undefined;
|
|
165
|
+
const dominantColor = (node.dominantColor ?? (asset.meta?.dominantColor as string | undefined)) as
|
|
166
|
+
| string
|
|
167
|
+
| undefined;
|
|
158
168
|
|
|
159
169
|
let placeholderStyle = "";
|
|
160
170
|
if (blurhash) {
|
|
@@ -200,6 +200,13 @@ function convertPMNode(node: PMNode): PTBlock | PTBlock[] | null {
|
|
|
200
200
|
}
|
|
201
201
|
case "image": {
|
|
202
202
|
const provider = attrStrOpt(node.attrs, "provider");
|
|
203
|
+
const blurhash = attrStrOpt(node.attrs, "blurhash");
|
|
204
|
+
const dominantColor = attrStrOpt(node.attrs, "dominantColor");
|
|
205
|
+
// Persist LQIP as first-class block fields (matching the image-field
|
|
206
|
+
// MediaValue path) rather than nesting in `asset.meta`, so read sites
|
|
207
|
+
// and normalize don't need a dual-shape fallback. `asset.meta` is left
|
|
208
|
+
// to carry only provider-specific data — it isn't reconstructed here,
|
|
209
|
+
// so non-LQIP meta keys are never silently dropped on editor round-trip.
|
|
203
210
|
return {
|
|
204
211
|
_type: "image",
|
|
205
212
|
_key: k(),
|
|
@@ -212,6 +219,8 @@ function convertPMNode(node: PMNode): PTBlock | PTBlock[] | null {
|
|
|
212
219
|
caption: attrStrOpt(node.attrs, "caption") ?? attrStrOpt(node.attrs, "title"),
|
|
213
220
|
width: attrNum(node.attrs, "width"),
|
|
214
221
|
height: attrNum(node.attrs, "height"),
|
|
222
|
+
...(blurhash ? { blurhash } : {}),
|
|
223
|
+
...(dominantColor ? { dominantColor } : {}),
|
|
215
224
|
displayWidth: attrNum(node.attrs, "displayWidth"),
|
|
216
225
|
displayHeight: attrNum(node.attrs, "displayHeight"),
|
|
217
226
|
};
|
|
@@ -416,16 +425,38 @@ function convertPTBlock(block: PTBlock): JSONContent | null {
|
|
|
416
425
|
}
|
|
417
426
|
if (block._type === "image") {
|
|
418
427
|
const ib = block as PTBlock & {
|
|
419
|
-
asset?: {
|
|
428
|
+
asset?: {
|
|
429
|
+
_ref?: string;
|
|
430
|
+
url?: string;
|
|
431
|
+
provider?: string;
|
|
432
|
+
meta?: Record<string, unknown>;
|
|
433
|
+
};
|
|
420
434
|
url?: string;
|
|
421
435
|
alt?: string;
|
|
422
436
|
caption?: string;
|
|
423
437
|
width?: number;
|
|
424
438
|
height?: number;
|
|
439
|
+
/** LQIP — first-class field (legacy snapshots keep it in `asset.meta`). */
|
|
440
|
+
blurhash?: string;
|
|
441
|
+
dominantColor?: string;
|
|
425
442
|
displayWidth?: number;
|
|
426
443
|
displayHeight?: number;
|
|
427
444
|
};
|
|
428
445
|
const asset = ib.asset;
|
|
446
|
+
const meta = asset?.meta;
|
|
447
|
+
// Prefer first-class LQIP fields; fall back to `asset.meta` for legacy.
|
|
448
|
+
const blurhash =
|
|
449
|
+
typeof ib.blurhash === "string"
|
|
450
|
+
? ib.blurhash
|
|
451
|
+
: typeof meta?.blurhash === "string"
|
|
452
|
+
? meta.blurhash
|
|
453
|
+
: null;
|
|
454
|
+
const dominantColor =
|
|
455
|
+
typeof ib.dominantColor === "string"
|
|
456
|
+
? ib.dominantColor
|
|
457
|
+
: typeof meta?.dominantColor === "string"
|
|
458
|
+
? meta.dominantColor
|
|
459
|
+
: null;
|
|
429
460
|
return {
|
|
430
461
|
type: "image",
|
|
431
462
|
attrs: {
|
|
@@ -437,6 +468,8 @@ function convertPTBlock(block: PTBlock): JSONContent | null {
|
|
|
437
468
|
provider: asset?.provider,
|
|
438
469
|
width: ib.width,
|
|
439
470
|
height: ib.height,
|
|
471
|
+
blurhash,
|
|
472
|
+
dominantColor,
|
|
440
473
|
displayWidth: ib.displayWidth,
|
|
441
474
|
displayHeight: ib.displayHeight,
|
|
442
475
|
},
|
|
@@ -1145,6 +1178,8 @@ interface MediaItemData {
|
|
|
1145
1178
|
storageKey?: string;
|
|
1146
1179
|
width?: number;
|
|
1147
1180
|
height?: number;
|
|
1181
|
+
blurhash?: string;
|
|
1182
|
+
dominantColor?: string;
|
|
1148
1183
|
alt?: string;
|
|
1149
1184
|
provider?: string;
|
|
1150
1185
|
previewUrl?: string;
|
|
@@ -1224,6 +1259,8 @@ function InlineMediaPicker({
|
|
|
1224
1259
|
storageKey?: string;
|
|
1225
1260
|
width?: number;
|
|
1226
1261
|
height?: number;
|
|
1262
|
+
blurhash?: string;
|
|
1263
|
+
dominantColor?: string;
|
|
1227
1264
|
alt?: string;
|
|
1228
1265
|
meta?: Record<string, unknown>;
|
|
1229
1266
|
}>;
|
|
@@ -1239,6 +1276,8 @@ function InlineMediaPicker({
|
|
|
1239
1276
|
storageKey: item.storageKey,
|
|
1240
1277
|
width: item.width,
|
|
1241
1278
|
height: item.height,
|
|
1279
|
+
blurhash: item.blurhash,
|
|
1280
|
+
dominantColor: item.dominantColor,
|
|
1242
1281
|
alt: item.alt,
|
|
1243
1282
|
provider: activeProvider === "local" ? undefined : activeProvider,
|
|
1244
1283
|
previewUrl: item.previewUrl,
|
|
@@ -1319,6 +1358,8 @@ function InlineMediaPicker({
|
|
|
1319
1358
|
storageKey: raw.storageKey,
|
|
1320
1359
|
width: raw.width || dims.width,
|
|
1321
1360
|
height: raw.height || dims.height,
|
|
1361
|
+
blurhash: raw.blurhash,
|
|
1362
|
+
dominantColor: raw.dominantColor,
|
|
1322
1363
|
alt: raw.alt,
|
|
1323
1364
|
};
|
|
1324
1365
|
} else {
|
|
@@ -1339,6 +1380,8 @@ function InlineMediaPicker({
|
|
|
1339
1380
|
url: raw.previewUrl || "",
|
|
1340
1381
|
width: raw.width || dims.width,
|
|
1341
1382
|
height: raw.height || dims.height,
|
|
1383
|
+
blurhash: raw.blurhash,
|
|
1384
|
+
dominantColor: raw.dominantColor,
|
|
1342
1385
|
alt: raw.alt,
|
|
1343
1386
|
provider: activeProvider,
|
|
1344
1387
|
previewUrl: raw.previewUrl,
|
|
@@ -1854,6 +1897,8 @@ export function InlinePortableTextEditor({
|
|
|
1854
1897
|
provider: { default: null },
|
|
1855
1898
|
width: { default: null },
|
|
1856
1899
|
height: { default: null },
|
|
1900
|
+
blurhash: { default: null },
|
|
1901
|
+
dominantColor: { default: null },
|
|
1857
1902
|
};
|
|
1858
1903
|
},
|
|
1859
1904
|
}),
|
|
@@ -1924,6 +1969,8 @@ export function InlinePortableTextEditor({
|
|
|
1924
1969
|
mediaId: item.id,
|
|
1925
1970
|
width: item.width,
|
|
1926
1971
|
height: item.height,
|
|
1972
|
+
blurhash: item.blurhash,
|
|
1973
|
+
dominantColor: item.dominantColor,
|
|
1927
1974
|
})
|
|
1928
1975
|
.run();
|
|
1929
1976
|
setMediaPickerOpen(false);
|
|
@@ -199,6 +199,15 @@ async function rebuildTaxonomies(db: Kysely<unknown>, defaultLocale: string): Pr
|
|
|
199
199
|
.on("taxonomies")
|
|
200
200
|
.column("translation_group")
|
|
201
201
|
.execute();
|
|
202
|
+
// Dropping the old table dropped idx_taxonomies_parent (from 015); recreate
|
|
203
|
+
// it here for parity with rebuildMenuItems/rebuildContentTaxonomies. Existing
|
|
204
|
+
// installs recover it via migration 047.
|
|
205
|
+
await db.schema
|
|
206
|
+
.createIndex("idx_taxonomies_parent")
|
|
207
|
+
.ifNotExists()
|
|
208
|
+
.on("taxonomies")
|
|
209
|
+
.column("parent_id")
|
|
210
|
+
.execute();
|
|
202
211
|
}
|
|
203
212
|
|
|
204
213
|
async function rebuildTaxonomyDefs(db: Kysely<unknown>, defaultLocale: string): Promise<void> {
|
|
@@ -569,6 +578,13 @@ async function rebuildTaxonomiesDown(db: Kysely<unknown>): Promise<void> {
|
|
|
569
578
|
await db.schema.dropTable("taxonomies").execute();
|
|
570
579
|
await sql`ALTER TABLE taxonomies_old RENAME TO taxonomies`.execute(db);
|
|
571
580
|
await db.schema.createIndex("idx_taxonomies_name").on("taxonomies").column("name").execute();
|
|
581
|
+
// Restore the pre-036 (migration 015) parent index so the rollback is faithful.
|
|
582
|
+
await db.schema
|
|
583
|
+
.createIndex("idx_taxonomies_parent")
|
|
584
|
+
.ifNotExists()
|
|
585
|
+
.on("taxonomies")
|
|
586
|
+
.column("parent_id")
|
|
587
|
+
.execute();
|
|
572
588
|
}
|
|
573
589
|
|
|
574
590
|
async function rebuildTaxonomyDefsDown(db: Kysely<unknown>): Promise<void> {
|