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.
Files changed (422) hide show
  1. package/dist/api/route-utils.d.mts +3 -3
  2. package/dist/api/route-utils.mjs +12 -11
  3. package/dist/api/route-utils.mjs.map +1 -1
  4. package/dist/api/schemas/index.d.mts +1 -1
  5. package/dist/api/schemas/index.mjs +2 -2
  6. package/dist/{api-d_0wi9fW.mjs → api-BMfveMoB.mjs} +24 -20
  7. package/dist/api-BMfveMoB.mjs.map +1 -0
  8. package/dist/{apply-BE9UaWX0.mjs → apply--IVNSdXn.mjs} +19 -16
  9. package/dist/apply--IVNSdXn.mjs.map +1 -0
  10. package/dist/astro/index.d.mts +9 -9
  11. package/dist/astro/index.mjs +3 -2
  12. package/dist/astro/index.mjs.map +1 -1
  13. package/dist/astro/middleware/auth.d.mts +8 -8
  14. package/dist/astro/middleware/auth.mjs +2 -2
  15. package/dist/astro/middleware/redirect.mjs +4 -4
  16. package/dist/astro/middleware.d.mts +1 -1
  17. package/dist/astro/middleware.d.mts.map +1 -1
  18. package/dist/astro/middleware.mjs +99 -70
  19. package/dist/astro/middleware.mjs.map +1 -1
  20. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +5 -5
  21. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +5 -5
  22. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +2 -2
  23. package/dist/astro/routes/api/admin/api-tokens/index.mjs +3 -3
  24. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +3 -3
  25. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +6 -6
  26. package/dist/astro/routes/api/admin/byline-fields/index.mjs +6 -6
  27. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +6 -6
  28. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +12 -12
  29. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +12 -12
  30. package/dist/astro/routes/api/admin/bylines/index.mjs +12 -12
  31. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +10 -10
  32. package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -6
  33. package/dist/astro/routes/api/admin/comments/bulk.mjs +9 -9
  34. package/dist/astro/routes/api/admin/comments/counts.mjs +6 -6
  35. package/dist/astro/routes/api/admin/comments/index.mjs +9 -9
  36. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
  37. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  38. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +3 -3
  39. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +3 -3
  40. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +31 -31
  41. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +31 -31
  42. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +31 -31
  43. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +31 -31
  44. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +31 -31
  45. package/dist/astro/routes/api/admin/plugins/index.mjs +31 -31
  46. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
  47. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +31 -31
  48. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +31 -31
  49. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +31 -31
  50. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +31 -31
  51. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +32 -32
  52. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +31 -31
  53. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +32 -32
  54. package/dist/astro/routes/api/admin/plugins/updates.mjs +31 -31
  55. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +31 -31
  56. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
  57. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +31 -31
  58. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
  59. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
  60. package/dist/astro/routes/api/admin/users/_id_/index.mjs +5 -5
  61. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +2 -2
  62. package/dist/astro/routes/api/admin/users/index.mjs +5 -5
  63. package/dist/astro/routes/api/auth/dev-bypass.mjs +3 -3
  64. package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
  65. package/dist/astro/routes/api/auth/invite/complete.mjs +5 -5
  66. package/dist/astro/routes/api/auth/invite/index.mjs +5 -5
  67. package/dist/astro/routes/api/auth/invite/register-options.mjs +5 -5
  68. package/dist/astro/routes/api/auth/logout.mjs +2 -2
  69. package/dist/astro/routes/api/auth/magic-link/send.mjs +6 -6
  70. package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
  71. package/dist/astro/routes/api/auth/me.mjs +6 -6
  72. package/dist/astro/routes/api/auth/passkey/_id_.mjs +5 -5
  73. package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
  74. package/dist/astro/routes/api/auth/passkey/options.mjs +6 -6
  75. package/dist/astro/routes/api/auth/passkey/register/options.mjs +5 -5
  76. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +5 -5
  77. package/dist/astro/routes/api/auth/passkey/verify.mjs +5 -5
  78. package/dist/astro/routes/api/auth/signup/complete.mjs +5 -5
  79. package/dist/astro/routes/api/auth/signup/request.mjs +6 -6
  80. package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
  81. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +9 -9
  82. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +9 -9
  83. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
  84. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
  85. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
  86. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
  87. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +6 -6
  88. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +6 -6
  89. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
  90. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
  91. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +6 -6
  92. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +11 -11
  93. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
  94. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
  95. package/dist/astro/routes/api/content/_collection_/_id_.mjs +6 -6
  96. package/dist/astro/routes/api/content/_collection_/authors.mjs +3 -3
  97. package/dist/astro/routes/api/content/_collection_/index.mjs +6 -6
  98. package/dist/astro/routes/api/content/_collection_/trash.mjs +6 -6
  99. package/dist/astro/routes/api/dashboard.mjs +8 -8
  100. package/dist/astro/routes/api/dev/emails.mjs +2 -2
  101. package/dist/astro/routes/api/import/probe.d.mts +2 -2
  102. package/dist/astro/routes/api/import/probe.mjs +6 -6
  103. package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
  104. package/dist/astro/routes/api/import/wordpress/execute.d.mts +8 -8
  105. package/dist/astro/routes/api/import/wordpress/execute.mjs +9 -9
  106. package/dist/astro/routes/api/import/wordpress/media.d.mts +13 -1
  107. package/dist/astro/routes/api/import/wordpress/media.d.mts.map +1 -1
  108. package/dist/astro/routes/api/import/wordpress/media.mjs +13 -9
  109. package/dist/astro/routes/api/import/wordpress/media.mjs.map +1 -1
  110. package/dist/astro/routes/api/import/wordpress/prepare.mjs +8 -8
  111. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +8 -8
  112. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +6 -6
  113. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +9 -9
  114. package/dist/astro/routes/api/manifest.mjs +3 -3
  115. package/dist/astro/routes/api/mcp.mjs +36 -36
  116. package/dist/astro/routes/api/media/_id_/confirm.d.mts.map +1 -1
  117. package/dist/astro/routes/api/media/_id_/confirm.mjs +44 -8
  118. package/dist/astro/routes/api/media/_id_/confirm.mjs.map +1 -1
  119. package/dist/astro/routes/api/media/_id_.mjs +6 -6
  120. package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
  121. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
  122. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
  123. package/dist/astro/routes/api/media/providers/index.mjs +3 -3
  124. package/dist/astro/routes/api/media/upload-url.mjs +8 -8
  125. package/dist/astro/routes/api/media.mjs +22 -21
  126. package/dist/astro/routes/api/media.mjs.map +1 -1
  127. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +8 -8
  128. package/dist/astro/routes/api/menus/_name_/items.mjs +8 -8
  129. package/dist/astro/routes/api/menus/_name_/reorder.mjs +8 -8
  130. package/dist/astro/routes/api/menus/_name_/translations.mjs +8 -8
  131. package/dist/astro/routes/api/menus/_name_.mjs +8 -8
  132. package/dist/astro/routes/api/menus/index.mjs +8 -8
  133. package/dist/astro/routes/api/oauth/device/authorize.mjs +3 -3
  134. package/dist/astro/routes/api/oauth/device/code.mjs +4 -4
  135. package/dist/astro/routes/api/oauth/device/token.mjs +4 -4
  136. package/dist/astro/routes/api/oauth/register.mjs +2 -2
  137. package/dist/astro/routes/api/oauth/token/refresh.mjs +3 -3
  138. package/dist/astro/routes/api/oauth/token/revoke.mjs +3 -3
  139. package/dist/astro/routes/api/oauth/token.mjs +2 -2
  140. package/dist/astro/routes/api/openapi.json.mjs +3 -3
  141. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +3 -3
  142. package/dist/astro/routes/api/redirects/404s/index.mjs +8 -8
  143. package/dist/astro/routes/api/redirects/404s/summary.mjs +8 -8
  144. package/dist/astro/routes/api/redirects/_id_.mjs +9 -9
  145. package/dist/astro/routes/api/redirects/index.mjs +9 -9
  146. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
  147. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
  148. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +31 -31
  149. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +31 -31
  150. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +31 -31
  151. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +31 -31
  152. package/dist/astro/routes/api/schema/collections/index.mjs +31 -31
  153. package/dist/astro/routes/api/schema/index.mjs +6 -6
  154. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +31 -31
  155. package/dist/astro/routes/api/schema/orphans/index.mjs +31 -31
  156. package/dist/astro/routes/api/search/enable.mjs +9 -9
  157. package/dist/astro/routes/api/search/index.mjs +8 -8
  158. package/dist/astro/routes/api/search/rebuild.mjs +9 -9
  159. package/dist/astro/routes/api/search/stats.mjs +6 -6
  160. package/dist/astro/routes/api/search/suggest.mjs +8 -8
  161. package/dist/astro/routes/api/sections/_slug_.mjs +8 -8
  162. package/dist/astro/routes/api/sections/index.mjs +8 -8
  163. package/dist/astro/routes/api/settings/email.mjs +4 -4
  164. package/dist/astro/routes/api/settings.mjs +11 -11
  165. package/dist/astro/routes/api/setup/admin-verify.mjs +5 -5
  166. package/dist/astro/routes/api/setup/admin.mjs +5 -5
  167. package/dist/astro/routes/api/setup/dev-bypass.mjs +17 -17
  168. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  169. package/dist/astro/routes/api/setup/index.mjs +20 -20
  170. package/dist/astro/routes/api/setup/status.mjs +3 -3
  171. package/dist/astro/routes/api/snapshot.mjs +3 -3
  172. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +11 -11
  173. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +11 -11
  174. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +11 -11
  175. package/dist/astro/routes/api/taxonomies/index.mjs +11 -11
  176. package/dist/astro/routes/api/themes/preview.mjs +3 -3
  177. package/dist/astro/routes/api/typegen.mjs +5 -5
  178. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +6 -6
  179. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +8 -8
  180. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +8 -8
  181. package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
  182. package/dist/astro/routes/api/widget-areas/index.mjs +8 -8
  183. package/dist/astro/routes/api/widget-components.mjs +2 -2
  184. package/dist/astro/routes/robots.txt.mjs +5 -5
  185. package/dist/astro/routes/sitemap-_collection_.xml.mjs +11 -10
  186. package/dist/astro/routes/sitemap-_collection_.xml.mjs.map +1 -1
  187. package/dist/astro/routes/sitemap.xml.mjs +6 -6
  188. package/dist/astro/types.d.mts +10 -10
  189. package/dist/{authorize-B__u-iZJ.mjs → authorize-BZnOGJFo.mjs} +2 -2
  190. package/dist/{authorize-B__u-iZJ.mjs.map → authorize-BZnOGJFo.mjs.map} +1 -1
  191. package/dist/{byline-C-YajfV7.mjs → byline-LL8lyqdb.mjs} +6 -6
  192. package/dist/{byline-C-YajfV7.mjs.map → byline-LL8lyqdb.mjs.map} +1 -1
  193. package/dist/{byline-fields-Ca56gaT6.d.mts → byline-fields-KD3JFRuH.d.mts} +24 -6
  194. package/dist/byline-fields-KD3JFRuH.d.mts.map +1 -0
  195. package/dist/{byline-fields-B-KXkg5R.mjs → byline-fields-eyJMblSr.mjs} +2 -2
  196. package/dist/{byline-fields-B-KXkg5R.mjs.map → byline-fields-eyJMblSr.mjs.map} +1 -1
  197. package/dist/{bylines-D4KADDAi.mjs → bylines-BmJ7Vg7J.mjs} +4 -4
  198. package/dist/{bylines-D4KADDAi.mjs.map → bylines-BmJ7Vg7J.mjs.map} +1 -1
  199. package/dist/{bylines-BMtJnPnG.mjs → bylines-DE47vP8d.mjs} +6 -6
  200. package/dist/{bylines-BMtJnPnG.mjs.map → bylines-DE47vP8d.mjs.map} +1 -1
  201. package/dist/{cache-BUJe2w6Z.mjs → cache-D9jF5bYw.mjs} +2 -2
  202. package/dist/{cache-BUJe2w6Z.mjs.map → cache-D9jF5bYw.mjs.map} +1 -1
  203. package/dist/{chunks-BHgcHh2C.mjs → chunks-OCG163rN.mjs} +2 -2
  204. package/dist/{chunks-BHgcHh2C.mjs.map → chunks-OCG163rN.mjs.map} +1 -1
  205. package/dist/cli/index.mjs +17 -17
  206. package/dist/{comment-BDx50EX5.mjs → comment-D9BLDpZI.mjs} +3 -3
  207. package/dist/{comment-BDx50EX5.mjs.map → comment-D9BLDpZI.mjs.map} +1 -1
  208. package/dist/{comment-reaction-Gv_pgfI3.mjs → comment-reaction-Drl9SkNu.mjs} +2 -2
  209. package/dist/{comment-reaction-Gv_pgfI3.mjs.map → comment-reaction-Drl9SkNu.mjs.map} +1 -1
  210. package/dist/{comments-CXHuQ6jI.mjs → comments-BjJ9SKGU.mjs} +3 -3
  211. package/dist/{comments-CXHuQ6jI.mjs.map → comments-BjJ9SKGU.mjs.map} +1 -1
  212. package/dist/{content-PoVGKjp0.mjs → content-ncUWWjna.mjs} +10 -8
  213. package/dist/content-ncUWWjna.mjs.map +1 -0
  214. package/dist/{context-Dvws41_A.mjs → context-C_g-iJVA.mjs} +14 -8
  215. package/dist/context-C_g-iJVA.mjs.map +1 -0
  216. package/dist/{dashboard-DsYU8JKS.mjs → dashboard-CotyqQrW.mjs} +4 -4
  217. package/dist/{dashboard-DsYU8JKS.mjs.map → dashboard-CotyqQrW.mjs.map} +1 -1
  218. package/dist/db/index.d.mts +2 -2
  219. package/dist/db/index.mjs +1 -1
  220. package/dist/{db-errors-UXB1CK42.mjs → db-errors-CK46D-ly.mjs} +1 -1
  221. package/dist/{db-errors-UXB1CK42.mjs.map → db-errors-CK46D-ly.mjs.map} +1 -1
  222. package/dist/{placeholder-B0s2mWu5.mjs → enrich-BQ0mxJRs.mjs} +61 -10
  223. package/dist/enrich-BQ0mxJRs.mjs.map +1 -0
  224. package/dist/{error-lq_fL-RS.mjs → error-rVGiqWDt.mjs} +3 -2
  225. package/dist/error-rVGiqWDt.mjs.map +1 -0
  226. package/dist/{field-defs-cache-CYYx8xlG.mjs → field-defs-cache-ljXzJCfc.mjs} +2 -2
  227. package/dist/{field-defs-cache-CYYx8xlG.mjs.map → field-defs-cache-ljXzJCfc.mjs.map} +1 -1
  228. package/dist/{fts-manager-DW_NR-Fs.mjs → fts-manager-BbCCbEKA.mjs} +2 -2
  229. package/dist/{fts-manager-DW_NR-Fs.mjs.map → fts-manager-BbCCbEKA.mjs.map} +1 -1
  230. package/dist/{index-BroXT-vD.d.mts → index-BRNabKrx.d.mts} +53 -15
  231. package/dist/index-BRNabKrx.d.mts.map +1 -0
  232. package/dist/{index-qbL02uhs.d.mts → index-Dt0OKJB4.d.mts} +2 -2
  233. package/dist/{index-qbL02uhs.d.mts.map → index-Dt0OKJB4.d.mts.map} +1 -1
  234. package/dist/index.d.mts +12 -12
  235. package/dist/index.mjs +43 -43
  236. package/dist/{load-DMFi4n1p.mjs → load-DMjXghbZ.mjs} +2 -2
  237. package/dist/{load-DMFi4n1p.mjs.map → load-DMjXghbZ.mjs.map} +1 -1
  238. package/dist/{loader-BR2SgfEc.mjs → loader-BA_zhkS_.mjs} +82 -19
  239. package/dist/loader-BA_zhkS_.mjs.map +1 -0
  240. package/dist/{manifest-schema-BWKnQYQF.mjs → manifest-schema-kPGX7VS-.mjs} +4 -1
  241. package/dist/manifest-schema-kPGX7VS-.mjs.map +1 -0
  242. package/dist/media/image-endpoint.mjs +1 -1
  243. package/dist/media/index.d.mts +45 -2
  244. package/dist/media/index.d.mts.map +1 -1
  245. package/dist/media/index.mjs +5 -3
  246. package/dist/media/index.mjs.map +1 -1
  247. package/dist/media/local-runtime.d.mts +9 -9
  248. package/dist/media/local-runtime.d.mts.map +1 -1
  249. package/dist/media/local-runtime.mjs +17 -5
  250. package/dist/media/local-runtime.mjs.map +1 -1
  251. package/dist/{media-CTjJoJTh.mjs → media-C5t_eCnk.mjs} +4 -2
  252. package/dist/media-C5t_eCnk.mjs.map +1 -0
  253. package/dist/{media-allowlist-B77I5_bw.mjs → media-allowlist-Du8t1u6a.mjs} +2 -2
  254. package/dist/{media-allowlist-B77I5_bw.mjs.map → media-allowlist-Du8t1u6a.mjs.map} +1 -1
  255. package/dist/{menus-8O6xNcYW.mjs → menus-CBb6UrMU.mjs} +3 -3
  256. package/dist/{menus-8O6xNcYW.mjs.map → menus-CBb6UrMU.mjs.map} +1 -1
  257. package/dist/{menus-GvRSpgIA.mjs → menus-CYH09a41.mjs} +65 -41
  258. package/dist/menus-CYH09a41.mjs.map +1 -0
  259. package/dist/{mime-BTzvzJ8M.mjs → mime-CCEzze7W.mjs} +1 -1
  260. package/dist/{mime-BTzvzJ8M.mjs.map → mime-CCEzze7W.mjs.map} +1 -1
  261. package/dist/{normalize-ClbNIenm.mjs → normalize-CeglrBT9.mjs} +11 -2
  262. package/dist/normalize-CeglrBT9.mjs.map +1 -0
  263. package/dist/{object-cache-SEb2IM4Z.mjs → object-cache-CHbHv83-.mjs} +8 -4
  264. package/dist/object-cache-CHbHv83-.mjs.map +1 -0
  265. package/dist/{options-CqECG0gM.d.mts → options-41nCWqi9.d.mts} +3 -3
  266. package/dist/{options-CqECG0gM.d.mts.map → options-41nCWqi9.d.mts.map} +1 -1
  267. package/dist/page/index.d.mts +5 -2
  268. package/dist/page/index.d.mts.map +1 -1
  269. package/dist/page/index.mjs +3 -2
  270. package/dist/page/index.mjs.map +1 -1
  271. package/dist/{parse-DHI873qU.mjs → parse-BwAdS7FZ.mjs} +2 -2
  272. package/dist/{parse-DHI873qU.mjs.map → parse-BwAdS7FZ.mjs.map} +1 -1
  273. package/dist/{placeholder-QoI4gEzP.d.mts → placeholder-Cuce9U-m.d.mts} +20 -5
  274. package/dist/placeholder-Cuce9U-m.d.mts.map +1 -0
  275. package/dist/plugin-types.d.mts +5 -2
  276. package/dist/plugin-types.d.mts.map +1 -1
  277. package/dist/plugin-utils.d.mts +8 -8
  278. package/dist/plugins/adapt-sandbox-entry.d.mts +8 -8
  279. package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
  280. package/dist/{query-DZdlS6Cu.mjs → query-BpQb-blG.mjs} +15 -15
  281. package/dist/{query-DZdlS6Cu.mjs.map → query-BpQb-blG.mjs.map} +1 -1
  282. package/dist/{rate-limit-D1Y6VFVP.mjs → rate-limit-Dap6HMNM.mjs} +2 -2
  283. package/dist/{rate-limit-D1Y6VFVP.mjs.map → rate-limit-Dap6HMNM.mjs.map} +1 -1
  284. package/dist/{redirect-C1KVGYWe.mjs → redirect-_M0trWoo.mjs} +2 -2
  285. package/dist/{redirect-C1KVGYWe.mjs.map → redirect-_M0trWoo.mjs.map} +1 -1
  286. package/dist/{redirects-x9eFPeyi.mjs → redirects-Dcbuisoj.mjs} +2 -1
  287. package/dist/redirects-Dcbuisoj.mjs.map +1 -0
  288. package/dist/{redirects-DgnypVir.mjs → redirects-w0V9vj_j.mjs} +3 -3
  289. package/dist/{redirects-DgnypVir.mjs.map → redirects-w0V9vj_j.mjs.map} +1 -1
  290. package/dist/{registry-CJkCubOJ.mjs → registry-g8yYt4cC.mjs} +14 -4
  291. package/dist/registry-g8yYt4cC.mjs.map +1 -0
  292. package/dist/{runner-CEtq-6Sm.d.mts → runner-BbR3DfrL.d.mts} +2 -2
  293. package/dist/{runner-CEtq-6Sm.d.mts.map → runner-BbR3DfrL.d.mts.map} +1 -1
  294. package/dist/{runner-BsquExlK.mjs → runner-DPdt_Wpa.mjs} +250 -178
  295. package/dist/runner-DPdt_Wpa.mjs.map +1 -0
  296. package/dist/runtime.d.mts +9 -9
  297. package/dist/runtime.mjs +2 -2
  298. package/dist/{schema-DMjXuvNU.mjs → schema-DoLd3j2e.mjs} +5 -5
  299. package/dist/{schema-DMjXuvNU.mjs.map → schema-DoLd3j2e.mjs.map} +1 -1
  300. package/dist/{search-D1wKk3lc.mjs → search-BR0ZeZmh.mjs} +4 -4
  301. package/dist/{search-D1wKk3lc.mjs.map → search-BR0ZeZmh.mjs.map} +1 -1
  302. package/dist/{sections-CCMic8dB.mjs → sections-5J2YJPqJ.mjs} +3 -3
  303. package/dist/{sections-CCMic8dB.mjs.map → sections-5J2YJPqJ.mjs.map} +1 -1
  304. package/dist/seed/index.d.mts +2 -2
  305. package/dist/seed/index.mjs +15 -15
  306. package/dist/seo/index.d.mts +1 -1
  307. package/dist/{seo-DCEF-AVr.mjs → seo-BkhuuaaE.mjs} +25 -3
  308. package/dist/seo-BkhuuaaE.mjs.map +1 -0
  309. package/dist/{seo-3QrZ1EPT.mjs → seo-DKeXHkzE.mjs} +3 -3
  310. package/dist/{seo-3QrZ1EPT.mjs.map → seo-DKeXHkzE.mjs.map} +1 -1
  311. package/dist/{service-CQHK_10l.mjs → service-BbO2s8gT.mjs} +2 -2
  312. package/dist/{service-CQHK_10l.mjs.map → service-BbO2s8gT.mjs.map} +1 -1
  313. package/dist/{settings-C3Yi5Co3.mjs → settings-BMNr9Dmi.mjs} +3 -3
  314. package/dist/{settings-C3Yi5Co3.mjs.map → settings-BMNr9Dmi.mjs.map} +1 -1
  315. package/dist/{settings-D9rvaLuY.mjs → settings-DJeki-97.mjs} +4 -4
  316. package/dist/{settings-D9rvaLuY.mjs.map → settings-DJeki-97.mjs.map} +1 -1
  317. package/dist/{taxonomies-DhLaNJji.mjs → taxonomies-CM_fUqLU.mjs} +5 -4
  318. package/dist/taxonomies-CM_fUqLU.mjs.map +1 -0
  319. package/dist/{taxonomies-CurpLtNC.mjs → taxonomies-D13i2ctt.mjs} +76 -21
  320. package/dist/taxonomies-D13i2ctt.mjs.map +1 -0
  321. package/dist/{taxonomy-BBUlGug5.mjs → taxonomy-BU0LjdVb.mjs} +4 -4
  322. package/dist/{taxonomy-BBUlGug5.mjs.map → taxonomy-BU0LjdVb.mjs.map} +1 -1
  323. package/dist/{types-TTx8uP7V.d.mts → types-BYnDBVwe.d.mts} +27 -5
  324. package/dist/{types-TTx8uP7V.d.mts.map → types-BYnDBVwe.d.mts.map} +1 -1
  325. package/dist/{types-CTLJYMhN.d.mts → types-CNlaBFzx.d.mts} +11 -1
  326. package/dist/{types-CTLJYMhN.d.mts.map → types-CNlaBFzx.d.mts.map} +1 -1
  327. package/dist/{types-CmI1OyQg.mjs → types-CZgK3EMW.mjs} +2 -2
  328. package/dist/{types-CmI1OyQg.mjs.map → types-CZgK3EMW.mjs.map} +1 -1
  329. package/dist/{types-BWh2MjmA.d.mts → types-ETmO_jQr.d.mts} +2 -2
  330. package/dist/{types-BWh2MjmA.d.mts.map → types-ETmO_jQr.d.mts.map} +1 -1
  331. package/dist/types-tM44hEcf.mjs.map +1 -1
  332. package/dist/{types-B-gIxFCE.d.mts → types-xIfVRNLp.d.mts} +38 -1
  333. package/dist/types-xIfVRNLp.d.mts.map +1 -0
  334. package/dist/{user-JAuc8VUf.mjs → user-DkG2jKCp.mjs} +3 -3
  335. package/dist/{user-JAuc8VUf.mjs.map → user-DkG2jKCp.mjs.map} +1 -1
  336. package/dist/{validate-DOFrIb-c.d.mts → validate-CG0V0wNR.d.mts} +3 -3
  337. package/dist/{validate-DOFrIb-c.d.mts.map → validate-CG0V0wNR.d.mts.map} +1 -1
  338. package/dist/{validate-BzXHWnkV.mjs → validate-CuJ1Ps9j.mjs} +2 -2
  339. package/dist/{validate-BzXHWnkV.mjs.map → validate-CuJ1Ps9j.mjs.map} +1 -1
  340. package/dist/{validation-CY1gk4Ar.mjs → validation-CwUfTdGF.mjs} +6 -6
  341. package/dist/{validation-CY1gk4Ar.mjs.map → validation-CwUfTdGF.mjs.map} +1 -1
  342. package/dist/version-BoNBItP_.mjs +7 -0
  343. package/dist/{version-C0JPFUG8.mjs.map → version-BoNBItP_.mjs.map} +1 -1
  344. package/dist/{widgets-Dri0spI4.mjs → widgets-BfOZ-ajY.mjs} +2 -2
  345. package/dist/{widgets-Dri0spI4.mjs.map → widgets-BfOZ-ajY.mjs.map} +1 -1
  346. package/dist/{zod-generator-xEHODMJf.mjs → zod-generator-gFsRuNNy.mjs} +6 -3
  347. package/dist/zod-generator-gFsRuNNy.mjs.map +1 -0
  348. package/package.json +6 -6
  349. package/src/api/errors.ts +1 -0
  350. package/src/api/handlers/content.ts +4 -4
  351. package/src/api/handlers/seo.ts +30 -2
  352. package/src/api/handlers/taxonomies.ts +5 -1
  353. package/src/api/schemas/schema.ts +1 -0
  354. package/src/astro/integration/virtual-modules.ts +15 -0
  355. package/src/astro/middleware.ts +14 -1
  356. package/src/astro/routes/api/import/wordpress/media.ts +9 -3
  357. package/src/astro/routes/api/media/[id]/confirm.ts +62 -2
  358. package/src/astro/routes/api/media.ts +17 -19
  359. package/src/astro/routes/sitemap-[collection].xml.ts +5 -4
  360. package/src/components/EmDashHead.astro +23 -3
  361. package/src/components/EmDashImage.astro +4 -7
  362. package/src/components/Image.astro +14 -4
  363. package/src/components/InlinePortableTextEditor.tsx +48 -1
  364. package/src/components/JsonLdScript.astro +9 -0
  365. package/src/database/migrations/036_i18n_menus_and_taxonomies.ts +16 -0
  366. package/src/database/migrations/046_media_usage_index.ts +115 -0
  367. package/src/database/migrations/047_restore_taxonomy_parent_index.ts +29 -0
  368. package/src/database/migrations/runner.ts +4 -0
  369. package/src/database/repositories/content.ts +8 -5
  370. package/src/database/repositories/media-usage.ts +444 -0
  371. package/src/database/repositories/media.ts +9 -1
  372. package/src/database/types.ts +39 -0
  373. package/src/emdash-runtime.ts +72 -25
  374. package/src/index.ts +3 -0
  375. package/src/loader.ts +110 -31
  376. package/src/media/enrich.ts +78 -0
  377. package/src/media/index.ts +1 -0
  378. package/src/media/local-runtime.ts +19 -0
  379. package/src/media/normalize.ts +20 -1
  380. package/src/media/placeholder.ts +29 -9
  381. package/src/media/types.ts +14 -0
  382. package/src/media/usage/extractor.ts +302 -0
  383. package/src/media/usage/types.ts +46 -0
  384. package/src/object-cache/index.ts +8 -2
  385. package/src/page/metadata.ts +23 -3
  386. package/src/plugin-types.ts +12 -0
  387. package/src/plugins/context.ts +8 -0
  388. package/src/plugins/define-plugin.ts +14 -5
  389. package/src/plugins/hooks.ts +66 -33
  390. package/src/plugins/index.ts +6 -0
  391. package/src/plugins/manager.ts +33 -0
  392. package/src/plugins/manifest-schema.ts +3 -0
  393. package/src/plugins/types.ts +41 -2
  394. package/src/schema/registry.ts +25 -0
  395. package/src/schema/types.ts +10 -0
  396. package/src/schema/zod-generator.ts +9 -1
  397. package/src/seed/apply.ts +7 -0
  398. package/src/taxonomies/index.ts +121 -18
  399. package/dist/api-d_0wi9fW.mjs.map +0 -1
  400. package/dist/apply-BE9UaWX0.mjs.map +0 -1
  401. package/dist/byline-fields-Ca56gaT6.d.mts.map +0 -1
  402. package/dist/content-PoVGKjp0.mjs.map +0 -1
  403. package/dist/context-Dvws41_A.mjs.map +0 -1
  404. package/dist/error-lq_fL-RS.mjs.map +0 -1
  405. package/dist/index-BroXT-vD.d.mts.map +0 -1
  406. package/dist/loader-BR2SgfEc.mjs.map +0 -1
  407. package/dist/manifest-schema-BWKnQYQF.mjs.map +0 -1
  408. package/dist/media-CTjJoJTh.mjs.map +0 -1
  409. package/dist/menus-GvRSpgIA.mjs.map +0 -1
  410. package/dist/normalize-ClbNIenm.mjs.map +0 -1
  411. package/dist/object-cache-SEb2IM4Z.mjs.map +0 -1
  412. package/dist/placeholder-B0s2mWu5.mjs.map +0 -1
  413. package/dist/placeholder-QoI4gEzP.d.mts.map +0 -1
  414. package/dist/redirects-x9eFPeyi.mjs.map +0 -1
  415. package/dist/registry-CJkCubOJ.mjs.map +0 -1
  416. package/dist/runner-BsquExlK.mjs.map +0 -1
  417. package/dist/seo-DCEF-AVr.mjs.map +0 -1
  418. package/dist/taxonomies-CurpLtNC.mjs.map +0 -1
  419. package/dist/taxonomies-DhLaNJji.mjs.map +0 -1
  420. package/dist/types-B-gIxFCE.d.mts.map +0 -1
  421. package/dist/version-C0JPFUG8.mjs +0 -7
  422. package/dist/zod-generator-xEHODMJf.mjs.map +0 -1
@@ -9,6 +9,8 @@
9
9
  import { encode } from "blurhash";
10
10
  import { imageSize } from "image-size";
11
11
 
12
+ import { normalizeMime } from "./mime.js";
13
+
12
14
  export interface PlaceholderData {
13
15
  blurhash: string;
14
16
  dominantColor: string;
@@ -85,8 +87,12 @@ function extractDominantColor(data: Uint8Array, width: number, height: number):
85
87
 
86
88
  /**
87
89
  * Read image dimensions from headers without decoding pixel data.
90
+ * Returns null when the header cannot be parsed.
91
+ *
92
+ * Shared by every caller that needs pixel dimensions so the header is parsed
93
+ * once per buffer, not re-read inside generatePlaceholder.
88
94
  */
89
- function getImageDimensions(buffer: Uint8Array): { width: number; height: number } | null {
95
+ export function readDimensions(buffer: Uint8Array): { width: number; height: number } | null {
90
96
  try {
91
97
  const result = imageSize(buffer);
92
98
  if (result.width != null && result.height != null) {
@@ -102,23 +108,37 @@ function getImageDimensions(buffer: Uint8Array): { width: number; height: number
102
108
  * Generate blurhash and dominant color from an image buffer.
103
109
  * Returns null for non-image MIME types or on failure.
104
110
  *
105
- * @param dimensions - Optional pre-known dimensions. Used as a fallback when
106
- * image-size cannot parse the buffer (e.g. truncated headers). When the
107
- * decoded size (width * height * 4) exceeds MAX_DECODED_BYTES, placeholder
108
- * generation is skipped to avoid OOM on memory-constrained runtimes.
111
+ * @param dimensions - Optional pre-known dimensions. When present they are
112
+ * trusted verbatim (the caller has typically already read them via
113
+ * readDimensions); otherwise dimensions are read from this buffer's header.
114
+ * Generation is skipped (returns null) when no dimensions are available at
115
+ * all, or when the decoded size (width * height * 4) exceeds
116
+ * MAX_DECODED_BYTES — both guards avoid OOM from unbounded decodes on
117
+ * memory-constrained runtimes.
109
118
  */
110
119
  export async function generatePlaceholder(
111
120
  buffer: Uint8Array,
112
121
  mimeType: string,
113
122
  dimensions?: { width: number; height: number },
114
123
  ): Promise<PlaceholderData | null> {
115
- const format = SUPPORTED_TYPES[mimeType];
124
+ const format = SUPPORTED_TYPES[normalizeMime(mimeType)];
116
125
  if (!format) return null;
117
126
 
118
127
  try {
119
- // Safety net: skip decode if the image would exceed the memory budget
120
- const dims = getImageDimensions(buffer) ?? dimensions;
121
- if (dims && dims.width * dims.height * 4 > MAX_DECODED_BYTES) {
128
+ // Trust caller-supplied dimensions when present (the caller has usually
129
+ // already read them via readDimensions); otherwise read them from this
130
+ // buffer. The header is parsed at most once per call.
131
+ const dims = dimensions ?? readDimensions(buffer);
132
+
133
+ // Safety net: the decoders allocate the full RGBA buffer with no internal
134
+ // cap, so refuse to decode unless we can bound the output size. When we
135
+ // have no parseable header AND no known dimensions, the decoded size is
136
+ // unbounded — a crafted/truncated PNG whose header image-size can't read
137
+ // could still be decodable by upng-js, so we must bail to avoid OOM on
138
+ // memory-constrained runtimes. LQIP is progressive enhancement; missing
139
+ // it is preferable to crashing the request.
140
+ if (!dims) return null;
141
+ if (dims.width * dims.height * 4 > MAX_DECODED_BYTES) {
122
142
  return null;
123
143
  }
124
144
 
@@ -85,6 +85,10 @@ export interface MediaProviderItem {
85
85
  /** Dimensions (for images/video) */
86
86
  width?: number;
87
87
  height?: number;
88
+ /** LQIP blurhash placeholder (images only) */
89
+ blurhash?: string;
90
+ /** LQIP dominant-color placeholder, as a CSS color (images only) */
91
+ dominantColor?: string;
88
92
  /** Accessibility text */
89
93
  alt?: string;
90
94
  /** Preview URL for admin UI thumbnail */
@@ -126,6 +130,10 @@ export interface ImageEmbed {
126
130
  sizes?: string;
127
131
  width?: number;
128
132
  height?: number;
133
+ /** LQIP blurhash placeholder for rendering before the image loads */
134
+ blurhash?: string;
135
+ /** LQIP dominant-color placeholder, as a CSS color */
136
+ dominantColor?: string;
129
137
  alt?: string;
130
138
  /** Base URL without transforms, for responsive image generation */
131
139
  cdnBaseUrl?: string;
@@ -256,6 +264,10 @@ export interface MediaValue {
256
264
  mimeType?: string;
257
265
  width?: number;
258
266
  height?: number;
267
+ /** Cached LQIP blurhash placeholder (images only) */
268
+ blurhash?: string;
269
+ /** Cached LQIP dominant-color placeholder, as a CSS color (images only) */
270
+ dominantColor?: string;
259
271
  alt?: string;
260
272
 
261
273
  /** Provider-specific data needed for embedding */
@@ -273,6 +285,8 @@ export function mediaItemToValue(providerId: string, item: MediaProviderItem): M
273
285
  mimeType: item.mimeType,
274
286
  width: item.width,
275
287
  height: item.height,
288
+ blurhash: item.blurhash,
289
+ dominantColor: item.dominantColor,
276
290
  alt: item.alt,
277
291
  meta: item.meta,
278
292
  };
@@ -0,0 +1,302 @@
1
+ import { normalizeMime } from "../mime.js";
2
+ import type {
3
+ ExtractedMediaUsageOccurrence,
4
+ ExtractMediaUsageOccurrencesInput,
5
+ MediaKind,
6
+ MediaUsageExtractionSubField,
7
+ MediaUsageReferenceType,
8
+ } from "./types.js";
9
+
10
+ const INTERNAL_MEDIA_PREFIX = "/_emdash/api/media/file/";
11
+ const URL_LIKE_RE = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
12
+
13
+ interface MediaRef {
14
+ mediaId: string | null;
15
+ provider: string;
16
+ providerAssetId: string;
17
+ mediaKind: MediaKind | null;
18
+ mimeType: string | null;
19
+ }
20
+
21
+ interface AddOccurrenceInput {
22
+ fieldSlug: string;
23
+ fieldPath: string;
24
+ referenceType: MediaUsageReferenceType;
25
+ value: unknown;
26
+ fallbackKind: MediaKind | null;
27
+ }
28
+
29
+ export function extractMediaUsageOccurrences({
30
+ fields,
31
+ data,
32
+ }: ExtractMediaUsageOccurrencesInput): ExtractedMediaUsageOccurrence[] {
33
+ const occurrences: ExtractedMediaUsageOccurrence[] = [];
34
+ const seen = new Set<string>();
35
+
36
+ for (const field of fields) {
37
+ const value = data[field.slug];
38
+
39
+ if (field.type === "image") {
40
+ addOccurrence(occurrences, seen, {
41
+ fieldSlug: field.slug,
42
+ fieldPath: field.slug,
43
+ referenceType: "image_field",
44
+ value,
45
+ fallbackKind: "image",
46
+ });
47
+ continue;
48
+ }
49
+
50
+ if (field.type === "file") {
51
+ addOccurrence(occurrences, seen, {
52
+ fieldSlug: field.slug,
53
+ fieldPath: field.slug,
54
+ referenceType: "file_field",
55
+ value,
56
+ fallbackKind: null,
57
+ });
58
+ continue;
59
+ }
60
+
61
+ if (field.type === "repeater") {
62
+ extractRepeaterOccurrences(occurrences, seen, field.slug, value, field.validation?.subFields);
63
+ continue;
64
+ }
65
+
66
+ if (field.type === "portableText") {
67
+ extractPortableTextOccurrences(occurrences, seen, field.slug, value);
68
+ }
69
+ }
70
+
71
+ return occurrences;
72
+ }
73
+
74
+ function extractRepeaterOccurrences(
75
+ occurrences: ExtractedMediaUsageOccurrence[],
76
+ seen: Set<string>,
77
+ fieldSlug: string,
78
+ value: unknown,
79
+ subFields: readonly MediaUsageExtractionSubField[] | undefined,
80
+ ): void {
81
+ if (!Array.isArray(value) || !Array.isArray(subFields)) return;
82
+
83
+ for (const [itemIndex, item] of value.entries()) {
84
+ if (!isRecord(item)) continue;
85
+
86
+ for (const subField of subFields) {
87
+ if (subField.type !== "image" && subField.type !== "file") continue;
88
+
89
+ addOccurrence(occurrences, seen, {
90
+ fieldSlug,
91
+ fieldPath: `${fieldSlug}[${itemIndex}].${subField.slug}`,
92
+ referenceType: subField.type === "image" ? "image_field" : "file_field",
93
+ value: item[subField.slug],
94
+ fallbackKind: subField.type === "image" ? "image" : null,
95
+ });
96
+ }
97
+ }
98
+ }
99
+
100
+ function extractPortableTextOccurrences(
101
+ occurrences: ExtractedMediaUsageOccurrence[],
102
+ seen: Set<string>,
103
+ fieldSlug: string,
104
+ value: unknown,
105
+ ): void {
106
+ if (!Array.isArray(value)) return;
107
+
108
+ for (const [blockIndex, block] of value.entries()) {
109
+ if (!isRecord(block) || block._type !== "image" || !isRecord(block.asset)) continue;
110
+
111
+ const provider = normalizeProvider(block.asset.provider);
112
+ const ref = readPortableTextAssetRef(block.asset, provider);
113
+ if (!ref) continue;
114
+
115
+ addRefOccurrence(occurrences, seen, {
116
+ fieldSlug,
117
+ fieldPath: `${fieldSlug}[${blockIndex}].asset.${ref.key}`,
118
+ referenceType: "portable_text_image",
119
+ ref: buildMediaRef({
120
+ id: ref.id,
121
+ provider,
122
+ mimeType: normalizeMimeValue(block.asset.mimeType),
123
+ fallbackKind: "image",
124
+ }),
125
+ });
126
+ }
127
+ }
128
+
129
+ function addOccurrence(
130
+ occurrences: ExtractedMediaUsageOccurrence[],
131
+ seen: Set<string>,
132
+ input: AddOccurrenceInput,
133
+ ): void {
134
+ const ref = readMediaRef(input.value, input.fallbackKind);
135
+ if (!ref) return;
136
+
137
+ addRefOccurrence(occurrences, seen, {
138
+ fieldSlug: input.fieldSlug,
139
+ fieldPath: input.fieldPath,
140
+ referenceType: input.referenceType,
141
+ ref,
142
+ });
143
+ }
144
+
145
+ function addRefOccurrence(
146
+ occurrences: ExtractedMediaUsageOccurrence[],
147
+ seen: Set<string>,
148
+ input: {
149
+ fieldSlug: string;
150
+ fieldPath: string;
151
+ referenceType: MediaUsageReferenceType;
152
+ ref: MediaRef | null;
153
+ },
154
+ ): void {
155
+ if (!input.ref) return;
156
+
157
+ const occurrence: ExtractedMediaUsageOccurrence = {
158
+ fieldSlug: input.fieldSlug,
159
+ fieldPath: input.fieldPath,
160
+ occurrenceIndex: 0,
161
+ referenceType: input.referenceType,
162
+ mediaId: input.ref.mediaId,
163
+ provider: input.ref.provider,
164
+ providerAssetId: input.ref.providerAssetId,
165
+ mediaKind: input.ref.mediaKind,
166
+ mimeType: input.ref.mimeType,
167
+ };
168
+
169
+ const key = [
170
+ occurrence.fieldSlug,
171
+ occurrence.fieldPath,
172
+ occurrence.occurrenceIndex,
173
+ occurrence.referenceType,
174
+ occurrence.provider,
175
+ occurrence.providerAssetId,
176
+ occurrence.mediaId ?? "",
177
+ ].join("\0");
178
+
179
+ if (seen.has(key)) return;
180
+ seen.add(key);
181
+ occurrences.push(occurrence);
182
+ }
183
+
184
+ function readMediaRef(value: unknown, fallbackKind: MediaKind | null): MediaRef | null {
185
+ if (typeof value === "string") {
186
+ const id = normalizeLocalMediaId(value);
187
+ return id ? buildMediaRef({ id, provider: "local", mimeType: null, fallbackKind }) : null;
188
+ }
189
+
190
+ if (!isRecord(value)) return null;
191
+
192
+ const provider = normalizeProvider(value.provider);
193
+ const id = provider === "local" ? normalizeLocalMediaId(value.id) : normalizeStableId(value.id);
194
+ if (!id) return null;
195
+
196
+ return buildMediaRef({
197
+ id,
198
+ provider,
199
+ mimeType: normalizeMimeValue(value.mimeType),
200
+ fallbackKind,
201
+ });
202
+ }
203
+
204
+ function buildMediaRef(input: {
205
+ id: string;
206
+ provider: string;
207
+ mimeType: string | null;
208
+ fallbackKind: MediaKind | null;
209
+ }): MediaRef | null {
210
+ const provider = normalizeProvider(input.provider);
211
+ if (provider === "external") return null;
212
+
213
+ return {
214
+ mediaId: provider === "local" ? input.id : null,
215
+ provider,
216
+ providerAssetId: input.id,
217
+ mediaKind: mediaKindFromMime(input.mimeType) ?? input.fallbackKind,
218
+ mimeType: input.mimeType,
219
+ };
220
+ }
221
+
222
+ function readPortableTextAssetRef(
223
+ asset: Record<string, unknown>,
224
+ provider: string,
225
+ ): { key: "_ref" | "id"; id: string } | null {
226
+ const normalizeId = provider === "local" ? normalizeLocalMediaId : normalizeStableId;
227
+ const ref = normalizeId(asset._ref);
228
+ if (ref) return { key: "_ref", id: ref };
229
+
230
+ const id = normalizeId(asset.id);
231
+ if (id) return { key: "id", id };
232
+
233
+ return null;
234
+ }
235
+
236
+ function normalizeProvider(value: unknown): string {
237
+ const provider = readString(value)?.trim();
238
+ return provider || "local";
239
+ }
240
+
241
+ function normalizeLocalMediaId(value: unknown): string | null {
242
+ const id = normalizeStableId(value);
243
+ if (!id) return null;
244
+ return id.includes("/") ? null : id;
245
+ }
246
+
247
+ function normalizeStableId(value: unknown): string | null {
248
+ if (typeof value !== "string") return null;
249
+ const trimmed = value.trim();
250
+ if (!trimmed) return null;
251
+ if (URL_LIKE_RE.test(trimmed)) return null;
252
+ if (trimmed.startsWith(INTERNAL_MEDIA_PREFIX)) return null;
253
+ return trimmed;
254
+ }
255
+
256
+ function normalizeMimeValue(value: unknown): string | null {
257
+ if (typeof value !== "string") return null;
258
+ const normalized = normalizeMime(value);
259
+ return normalized.includes("/") ? normalized : null;
260
+ }
261
+
262
+ function mediaKindFromMime(mimeType: string | null): MediaKind | null {
263
+ if (!mimeType) return null;
264
+ if (mimeType.startsWith("image/")) return "image";
265
+ if (mimeType.startsWith("video/")) return "video";
266
+ if (mimeType.startsWith("audio/")) return "audio";
267
+ if (mimeType.startsWith("font/") || mimeType.startsWith("application/font-")) return "font";
268
+ if (mimeType.startsWith("text/")) return "text";
269
+ if (isDocumentMime(mimeType)) return "document";
270
+ if (isArchiveMime(mimeType)) return "archive";
271
+ return "other";
272
+ }
273
+
274
+ function isDocumentMime(mimeType: string): boolean {
275
+ return (
276
+ mimeType === "application/pdf" ||
277
+ mimeType === "application/msword" ||
278
+ mimeType === "application/rtf" ||
279
+ mimeType === "application/vnd.ms-excel" ||
280
+ mimeType === "application/vnd.ms-powerpoint" ||
281
+ mimeType.startsWith("application/vnd.openxmlformats-officedocument.")
282
+ );
283
+ }
284
+
285
+ function isArchiveMime(mimeType: string): boolean {
286
+ return (
287
+ mimeType === "application/zip" ||
288
+ mimeType === "application/gzip" ||
289
+ mimeType === "application/x-tar" ||
290
+ mimeType === "application/x-7z-compressed" ||
291
+ mimeType === "application/x-rar-compressed" ||
292
+ mimeType === "application/vnd.rar"
293
+ );
294
+ }
295
+
296
+ function readString(value: unknown): string | null {
297
+ return typeof value === "string" ? value : null;
298
+ }
299
+
300
+ function isRecord(value: unknown): value is Record<string, unknown> {
301
+ return typeof value === "object" && value !== null && !Array.isArray(value);
302
+ }
@@ -0,0 +1,46 @@
1
+ import type { FieldType } from "../../schema/types.js";
2
+
3
+ export type MediaKind =
4
+ | "image"
5
+ | "video"
6
+ | "audio"
7
+ | "document"
8
+ | "archive"
9
+ | "font"
10
+ | "text"
11
+ | "other";
12
+
13
+ export type MediaUsageReferenceType = "image_field" | "file_field" | "portable_text_image";
14
+
15
+ export interface MediaUsageExtractionSubField {
16
+ slug: string;
17
+ type: FieldType;
18
+ label?: string;
19
+ }
20
+
21
+ export interface MediaUsageExtractionValidation {
22
+ subFields?: readonly MediaUsageExtractionSubField[];
23
+ }
24
+
25
+ export interface MediaUsageExtractionField {
26
+ slug: string;
27
+ type: FieldType;
28
+ validation?: MediaUsageExtractionValidation | null;
29
+ }
30
+
31
+ export interface ExtractMediaUsageOccurrencesInput {
32
+ fields: readonly MediaUsageExtractionField[];
33
+ data: Record<string, unknown>;
34
+ }
35
+
36
+ export interface ExtractedMediaUsageOccurrence {
37
+ fieldSlug: string;
38
+ fieldPath: string;
39
+ occurrenceIndex: number;
40
+ referenceType: MediaUsageReferenceType;
41
+ mediaId: string | null;
42
+ provider: string;
43
+ providerAssetId: string;
44
+ mediaKind: MediaKind | null;
45
+ mimeType: string | null;
46
+ }
@@ -167,7 +167,7 @@ async function getBackend(): Promise<ObjectCacheBackend | null> {
167
167
  } catch (error) {
168
168
  // Importing the virtual module fails outside an Astro/Vite context
169
169
  // (e.g. unit tests, CLI). Treat as "no cache configured".
170
- if (import.meta.env.DEV) {
170
+ if (import.meta.env?.DEV) {
171
171
  console.warn("[object-cache] backend unavailable:", error);
172
172
  }
173
173
  holder.backend = null;
@@ -378,7 +378,7 @@ export async function cachedQuery<T>(options: CachedQueryOptions<T>): Promise<T>
378
378
  const encoded = encode({ e: currentEpochs, v: value } satisfies CacheEnvelope<T>);
379
379
  await backend.set(fullKey, encoded, ttl);
380
380
  } catch (error) {
381
- if (import.meta.env.DEV) {
381
+ if (import.meta.env?.DEV) {
382
382
  console.warn("[object-cache] set failed:", error);
383
383
  }
384
384
  }
@@ -388,6 +388,12 @@ export async function cachedQuery<T>(options: CachedQueryOptions<T>): Promise<T>
388
388
  return value;
389
389
  }
390
390
 
391
+ /** Whether object-cache reads are active for the current request. */
392
+ export async function isObjectCacheActive(): Promise<boolean> {
393
+ const backend = await getBackend();
394
+ return backend !== null && !shouldBypass();
395
+ }
396
+
391
397
  /**
392
398
  * Invalidate every cached value in `namespace` by bumping its epoch.
393
399
  *
@@ -20,6 +20,10 @@ export interface ResolvedPageMetadata {
20
20
  jsonld: Array<{ id?: string; json: string }>;
21
21
  }
22
22
 
23
+ interface RenderPageMetadataOptions {
24
+ includeJsonLd?: boolean;
25
+ }
26
+
23
27
  // ── Validation ──────────────────────────────────────────────────
24
28
 
25
29
  /** Schemes safe for use in link href attributes */
@@ -66,6 +70,16 @@ export function safeJsonLdSerialize(value: unknown): string {
66
70
  .replace(JSONLD_U2029_RE, "\\u2029");
67
71
  }
68
72
 
73
+ export async function createSha256CspHash(value: string): Promise<string> {
74
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
75
+ const bytes = new Uint8Array(digest);
76
+ let binary = "";
77
+ for (const byte of bytes) {
78
+ binary += String.fromCharCode(byte);
79
+ }
80
+ return `sha256-${btoa(binary)}`;
81
+ }
82
+
69
83
  // ── Merge / dedupe ──────────────────────────────────────────────
70
84
 
71
85
  /**
@@ -155,8 +169,12 @@ export function resolvePageMetadata(
155
169
  // ── HTML rendering ──────────────────────────────────────────────
156
170
 
157
171
  /** Render resolved metadata to an HTML string for embedding in <head> */
158
- export function renderPageMetadata(metadata: ResolvedPageMetadata): string {
172
+ export function renderPageMetadata(
173
+ metadata: ResolvedPageMetadata,
174
+ options: RenderPageMetadataOptions = {},
175
+ ): string {
159
176
  const parts: string[] = [];
177
+ const includeJsonLd = options.includeJsonLd ?? true;
160
178
 
161
179
  for (const m of metadata.meta) {
162
180
  parts.push(`<meta name="${escapeHtmlAttr(m.name)}" content="${escapeHtmlAttr(m.content)}">`);
@@ -177,8 +195,10 @@ export function renderPageMetadata(metadata: ResolvedPageMetadata): string {
177
195
  parts.push(tag);
178
196
  }
179
197
 
180
- for (const j of metadata.jsonld) {
181
- parts.push(`<script type="application/ld+json">${j.json}</script>`);
198
+ if (includeJsonLd) {
199
+ for (const j of metadata.jsonld) {
200
+ parts.push(`<script type="application/ld+json">${j.json}</script>`);
201
+ }
182
202
  }
183
203
 
184
204
  return parts.join("\n");
@@ -49,13 +49,19 @@ import type {
49
49
  CommentModerateHandler,
50
50
  ContentAfterDeleteHandler,
51
51
  ContentAfterPublishHandler,
52
+ ContentAfterRestoreHandler,
52
53
  ContentAfterSaveHandler,
54
+ ContentAfterScheduleHandler,
53
55
  ContentAfterUnpublishHandler,
56
+ ContentAfterUnscheduleHandler,
54
57
  ContentBeforeDeleteHandler,
55
58
  ContentBeforeSaveHandler,
56
59
  ContentDeleteEvent,
57
60
  ContentHookEvent,
58
61
  ContentPublishStateChangeEvent,
62
+ ContentRestoreStateChangeEvent,
63
+ ContentScheduleStateChangeEvent,
64
+ ContentStateChangeEvent,
59
65
  CronEvent,
60
66
  CronHandler,
61
67
  EmailAfterSendEvent,
@@ -97,6 +103,9 @@ export interface HookHandlers {
97
103
  "content:afterDelete": ContentAfterDeleteHandler;
98
104
  "content:afterPublish": ContentAfterPublishHandler;
99
105
  "content:afterUnpublish": ContentAfterUnpublishHandler;
106
+ "content:afterRestore": ContentAfterRestoreHandler;
107
+ "content:afterSchedule": ContentAfterScheduleHandler;
108
+ "content:afterUnschedule": ContentAfterUnscheduleHandler;
100
109
  "media:beforeUpload": MediaBeforeUploadHandler;
101
110
  "media:afterUpload": MediaAfterUploadHandler;
102
111
  cron: CronHandler;
@@ -226,6 +235,9 @@ export type {
226
235
  ContentDeleteEvent,
227
236
  ContentHookEvent,
228
237
  ContentPublishStateChangeEvent,
238
+ ContentRestoreStateChangeEvent,
239
+ ContentScheduleStateChangeEvent,
240
+ ContentStateChangeEvent,
229
241
  CronEvent,
230
242
  EmailAfterSendEvent,
231
243
  EmailBeforeSendEvent,
@@ -21,6 +21,7 @@ import {
21
21
  SsrfError,
22
22
  stripCredentialHeaders,
23
23
  } from "../import/ssrf.js";
24
+ import { enrichImageMetadata } from "../media/enrich.js";
24
25
  import { invalidateSiteSettingsCache } from "../settings/index.js";
25
26
  import type { Storage } from "../storage/types.js";
26
27
  import { CronAccessImpl } from "./cron.js";
@@ -508,6 +509,9 @@ export function createMediaAccessWithWrite(
508
509
  contentType,
509
510
  });
510
511
 
512
+ // Derive dimensions + LQIP placeholders (no-op for non-images).
513
+ const enriched = await enrichImageMetadata(new Uint8Array(bytes), contentType);
514
+
511
515
  // Create DB record — clean up storage on failure
512
516
  let media;
513
517
  try {
@@ -517,6 +521,10 @@ export function createMediaAccessWithWrite(
517
521
  size: bytes.byteLength,
518
522
  storageKey,
519
523
  status: "ready",
524
+ width: enriched.width,
525
+ height: enriched.height,
526
+ blurhash: enriched.blurhash,
527
+ dominantColor: enriched.dominantColor,
520
528
  });
521
529
  } catch (error) {
522
530
  try {
@@ -22,11 +22,6 @@ import type {
22
22
  PluginStorageConfig,
23
23
  } from "./types.js";
24
24
 
25
- // Plugin ID validation patterns
26
- const SIMPLE_ID = /^[a-z0-9-]+$/;
27
- const SCOPED_ID = /^@[a-z0-9-]+\/[a-z0-9-]+$/;
28
- const SEMVER_PATTERN = /^\d+\.\d+\.\d+/;
29
-
30
25
  /**
31
26
  * Define a native EmDash plugin.
32
27
  *
@@ -94,6 +89,20 @@ export function definePlugin<TStorage extends PluginStorageConfig>(
94
89
  function defineNativePlugin<TStorage extends PluginStorageConfig>(
95
90
  definition: PluginDefinition<TStorage>,
96
91
  ): ResolvedPlugin<TStorage> {
92
+ // Declared function-local (not module scope) on purpose. Under
93
+ // `ssr.noExternal` the worker entry can instantiate native plugins during a
94
+ // circular module init, reaching this function before module-scope consts
95
+ // initialize -> "Cannot access 'SIMPLE_ID' before initialization" -> every
96
+ // route 500s on Cloudflare Workers. Call-time consts evaluate after the
97
+ // literals are parsed, so the temporal dead zone cannot occur regardless of
98
+ // bundle ordering. See #1370.
99
+ // oxlint-disable-next-line e18e/prefer-static-regex -- call-time on purpose (see #1370)
100
+ const SIMPLE_ID = /^[a-z0-9-]+$/;
101
+ // oxlint-disable-next-line e18e/prefer-static-regex -- call-time on purpose (see #1370)
102
+ const SCOPED_ID = /^@[a-z0-9-]+\/[a-z0-9-]+$/;
103
+ // oxlint-disable-next-line e18e/prefer-static-regex -- call-time on purpose (see #1370)
104
+ const SEMVER_PATTERN = /^\d+\.\d+\.\d+/;
105
+
97
106
  const {
98
107
  id,
99
108
  version,