emdash 0.25.1 → 0.27.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 (418) hide show
  1. package/dist/{adapters-DAAjOID4.d.mts → adapters-u037EnTR.d.mts} +1 -1
  2. package/dist/{adapters-DAAjOID4.d.mts.map → adapters-u037EnTR.d.mts.map} +1 -1
  3. package/dist/api/route-utils.d.mts +3 -3
  4. package/dist/api/route-utils.mjs +11 -11
  5. package/dist/api/schemas/index.d.mts +1 -1
  6. package/dist/{api-D2_sbavn.mjs → api-80kycR9o.mjs} +86 -30
  7. package/dist/api-80kycR9o.mjs.map +1 -0
  8. package/dist/{apply-Bk2mAuLn.mjs → apply-DSypk6Iu.mjs} +20 -18
  9. package/dist/apply-DSypk6Iu.mjs.map +1 -0
  10. package/dist/astro/index.d.mts +11 -10
  11. package/dist/astro/index.d.mts.map +1 -1
  12. package/dist/astro/index.mjs +12 -3
  13. package/dist/astro/index.mjs.map +1 -1
  14. package/dist/astro/middleware/auth.d.mts +10 -9
  15. package/dist/astro/middleware/auth.d.mts.map +1 -1
  16. package/dist/astro/middleware/auth.mjs +4 -3
  17. package/dist/astro/middleware/auth.mjs.map +1 -1
  18. package/dist/astro/middleware/redirect.mjs +4 -4
  19. package/dist/astro/middleware.d.mts +2 -9
  20. package/dist/astro/middleware.d.mts.map +1 -1
  21. package/dist/astro/middleware.mjs +86 -85
  22. package/dist/astro/middleware.mjs.map +1 -1
  23. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +3 -3
  24. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +3 -3
  25. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +2 -2
  26. package/dist/astro/routes/api/admin/api-tokens/index.mjs +3 -3
  27. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +3 -3
  28. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +4 -4
  29. package/dist/astro/routes/api/admin/byline-fields/index.mjs +4 -4
  30. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +4 -4
  31. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +10 -10
  32. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +10 -10
  33. package/dist/astro/routes/api/admin/bylines/index.mjs +10 -10
  34. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +8 -8
  35. package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -6
  36. package/dist/astro/routes/api/admin/comments/bulk.mjs +7 -7
  37. package/dist/astro/routes/api/admin/comments/counts.mjs +6 -6
  38. package/dist/astro/routes/api/admin/comments/index.mjs +7 -7
  39. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
  40. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  41. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +3 -3
  42. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +3 -3
  43. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +29 -29
  44. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  45. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +29 -29
  46. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  47. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +29 -29
  48. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  49. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +28 -28
  50. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +28 -28
  51. package/dist/astro/routes/api/admin/plugins/index.mjs +29 -29
  52. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  53. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
  54. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +28 -28
  55. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +28 -28
  56. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +28 -28
  57. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +28 -28
  58. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +29 -29
  59. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +28 -28
  60. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +29 -29
  61. package/dist/astro/routes/api/admin/plugins/updates.mjs +28 -28
  62. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +28 -28
  63. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
  64. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +28 -28
  65. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
  66. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
  67. package/dist/astro/routes/api/admin/users/_id_/index.mjs +3 -3
  68. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +2 -2
  69. package/dist/astro/routes/api/admin/users/index.mjs +3 -3
  70. package/dist/astro/routes/api/auth/dev-bypass.mjs +3 -3
  71. package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
  72. package/dist/astro/routes/api/auth/invite/complete.mjs +3 -3
  73. package/dist/astro/routes/api/auth/invite/index.mjs +3 -3
  74. package/dist/astro/routes/api/auth/invite/register-options.mjs +3 -3
  75. package/dist/astro/routes/api/auth/logout.mjs +2 -2
  76. package/dist/astro/routes/api/auth/magic-link/send.mjs +4 -4
  77. package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
  78. package/dist/astro/routes/api/auth/me.mjs +4 -4
  79. package/dist/astro/routes/api/auth/passkey/_id_.mjs +3 -3
  80. package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
  81. package/dist/astro/routes/api/auth/passkey/options.mjs +4 -4
  82. package/dist/astro/routes/api/auth/passkey/register/options.mjs +3 -3
  83. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +3 -3
  84. package/dist/astro/routes/api/auth/passkey/verify.mjs +3 -3
  85. package/dist/astro/routes/api/auth/signup/complete.mjs +3 -3
  86. package/dist/astro/routes/api/auth/signup/request.mjs +4 -4
  87. package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
  88. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +7 -7
  89. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +7 -7
  90. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
  91. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
  92. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
  93. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
  94. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +4 -4
  95. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +4 -4
  96. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
  97. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
  98. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +4 -4
  99. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +9 -9
  100. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
  101. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
  102. package/dist/astro/routes/api/content/_collection_/_id_.mjs +4 -4
  103. package/dist/astro/routes/api/content/_collection_/authors.mjs +3 -3
  104. package/dist/astro/routes/api/content/_collection_/index.mjs +4 -4
  105. package/dist/astro/routes/api/content/_collection_/trash.mjs +4 -4
  106. package/dist/astro/routes/api/dashboard.mjs +8 -8
  107. package/dist/astro/routes/api/dev/emails.mjs +2 -2
  108. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  109. package/dist/astro/routes/api/import/probe.mjs +4 -4
  110. package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
  111. package/dist/astro/routes/api/import/wordpress/execute.d.mts +10 -9
  112. package/dist/astro/routes/api/import/wordpress/execute.d.mts.map +1 -1
  113. package/dist/astro/routes/api/import/wordpress/execute.mjs +9 -9
  114. package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -9
  115. package/dist/astro/routes/api/import/wordpress/media.d.mts.map +1 -1
  116. package/dist/astro/routes/api/import/wordpress/media.mjs +4 -4
  117. package/dist/astro/routes/api/import/wordpress/prepare.mjs +6 -6
  118. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +5 -5
  119. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  120. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +4 -4
  121. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +1 -1
  122. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +7 -7
  123. package/dist/astro/routes/api/manifest.d.mts.map +1 -1
  124. package/dist/astro/routes/api/manifest.mjs +13 -4
  125. package/dist/astro/routes/api/manifest.mjs.map +1 -1
  126. package/dist/astro/routes/api/mcp.mjs +34 -34
  127. package/dist/astro/routes/api/media/_id_/confirm.mjs +4 -4
  128. package/dist/astro/routes/api/media/_id_.mjs +4 -4
  129. package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
  130. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
  131. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
  132. package/dist/astro/routes/api/media/providers/index.mjs +3 -3
  133. package/dist/astro/routes/api/media/upload-url.mjs +4 -4
  134. package/dist/astro/routes/api/media.mjs +5 -5
  135. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +6 -6
  136. package/dist/astro/routes/api/menus/_name_/items.mjs +6 -6
  137. package/dist/astro/routes/api/menus/_name_/reorder.mjs +6 -6
  138. package/dist/astro/routes/api/menus/_name_/translations.mjs +6 -6
  139. package/dist/astro/routes/api/menus/_name_.mjs +6 -6
  140. package/dist/astro/routes/api/menus/index.mjs +6 -6
  141. package/dist/astro/routes/api/oauth/device/authorize.mjs +3 -3
  142. package/dist/astro/routes/api/oauth/device/code.mjs +4 -4
  143. package/dist/astro/routes/api/oauth/device/token.mjs +4 -4
  144. package/dist/astro/routes/api/oauth/register.mjs +2 -2
  145. package/dist/astro/routes/api/oauth/token/refresh.mjs +3 -3
  146. package/dist/astro/routes/api/oauth/token/revoke.mjs +3 -3
  147. package/dist/astro/routes/api/oauth/token.mjs +2 -2
  148. package/dist/astro/routes/api/openapi.json.mjs +2 -2
  149. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +3 -3
  150. package/dist/astro/routes/api/redirects/404s/index.mjs +6 -6
  151. package/dist/astro/routes/api/redirects/404s/summary.mjs +6 -6
  152. package/dist/astro/routes/api/redirects/_id_.mjs +7 -7
  153. package/dist/astro/routes/api/redirects/index.mjs +7 -7
  154. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
  155. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
  156. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +28 -28
  157. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +28 -28
  158. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +28 -28
  159. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +28 -28
  160. package/dist/astro/routes/api/schema/collections/index.mjs +28 -28
  161. package/dist/astro/routes/api/schema/index.mjs +6 -6
  162. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +28 -28
  163. package/dist/astro/routes/api/schema/orphans/index.mjs +28 -28
  164. package/dist/astro/routes/api/search/enable.mjs +7 -7
  165. package/dist/astro/routes/api/search/index.mjs +6 -6
  166. package/dist/astro/routes/api/search/rebuild.mjs +7 -7
  167. package/dist/astro/routes/api/search/stats.mjs +6 -6
  168. package/dist/astro/routes/api/search/suggest.mjs +6 -6
  169. package/dist/astro/routes/api/sections/_slug_.mjs +6 -6
  170. package/dist/astro/routes/api/sections/index.mjs +6 -6
  171. package/dist/astro/routes/api/settings/email.mjs +4 -4
  172. package/dist/astro/routes/api/settings.mjs +9 -9
  173. package/dist/astro/routes/api/setup/admin-verify.mjs +3 -3
  174. package/dist/astro/routes/api/setup/admin.mjs +3 -3
  175. package/dist/astro/routes/api/setup/dev-bypass.d.mts.map +1 -1
  176. package/dist/astro/routes/api/setup/dev-bypass.mjs +20 -18
  177. package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
  178. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  179. package/dist/astro/routes/api/setup/index.mjs +18 -18
  180. package/dist/astro/routes/api/setup/status.mjs +3 -3
  181. package/dist/astro/routes/api/snapshot.mjs +3 -3
  182. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +9 -9
  183. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +9 -9
  184. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +9 -9
  185. package/dist/astro/routes/api/taxonomies/index.mjs +9 -9
  186. package/dist/astro/routes/api/themes/preview.mjs +3 -3
  187. package/dist/astro/routes/api/typegen.mjs +5 -5
  188. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +4 -4
  189. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +6 -6
  190. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +6 -6
  191. package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
  192. package/dist/astro/routes/api/widget-areas/index.mjs +6 -6
  193. package/dist/astro/routes/api/widget-components.mjs +2 -2
  194. package/dist/astro/routes/robots.txt.mjs +5 -5
  195. package/dist/astro/routes/sitemap-_collection_.xml.mjs +10 -9
  196. package/dist/astro/routes/sitemap-_collection_.xml.mjs.map +1 -1
  197. package/dist/astro/routes/sitemap.xml.mjs +5 -5
  198. package/dist/astro/types.d.mts +12 -10
  199. package/dist/astro/types.d.mts.map +1 -1
  200. package/dist/auth/providers/github.d.mts +1 -1
  201. package/dist/auth/providers/google.d.mts +1 -1
  202. package/dist/{authorize-CuxZ0i2t.mjs → authorize-C3a_zGsM.mjs} +2 -2
  203. package/dist/{authorize-CuxZ0i2t.mjs.map → authorize-C3a_zGsM.mjs.map} +1 -1
  204. package/dist/{byline-C-YajfV7.mjs → byline-DEKSVPTy.mjs} +6 -6
  205. package/dist/{byline-C-YajfV7.mjs.map → byline-DEKSVPTy.mjs.map} +1 -1
  206. package/dist/{byline-fields-KD3JFRuH.d.mts → byline-fields-CtQ6blXd.d.mts} +32 -32
  207. package/dist/{byline-fields-KD3JFRuH.d.mts.map → byline-fields-CtQ6blXd.d.mts.map} +1 -1
  208. package/dist/{bylines-CPc_q2Vd.mjs → bylines-CiqgTHWF.mjs} +5 -5
  209. package/dist/{bylines-CPc_q2Vd.mjs.map → bylines-CiqgTHWF.mjs.map} +1 -1
  210. package/dist/{bylines-D4KADDAi.mjs → bylines-Ds4-otXl.mjs} +4 -4
  211. package/dist/{bylines-D4KADDAi.mjs.map → bylines-Ds4-otXl.mjs.map} +1 -1
  212. package/dist/{cache-C35VWD5D.mjs → cache-Ciu3e3AF.mjs} +2 -2
  213. package/dist/{cache-C35VWD5D.mjs.map → cache-Ciu3e3AF.mjs.map} +1 -1
  214. package/dist/{chunks-BHgcHh2C.mjs → chunks-CZFiauig.mjs} +2 -2
  215. package/dist/{chunks-BHgcHh2C.mjs.map → chunks-CZFiauig.mjs.map} +1 -1
  216. package/dist/cli/index.mjs +17 -17
  217. package/dist/cli/index.mjs.map +1 -1
  218. package/dist/client/cf-access.d.mts +1 -1
  219. package/dist/client/index.d.mts +1 -1
  220. package/dist/{comment-BDx50EX5.mjs → comment-CH1IrFFs.mjs} +3 -3
  221. package/dist/{comment-BDx50EX5.mjs.map → comment-CH1IrFFs.mjs.map} +1 -1
  222. package/dist/{comment-reaction-Gv_pgfI3.mjs → comment-reaction-kruNY-kP.mjs} +2 -2
  223. package/dist/{comment-reaction-Gv_pgfI3.mjs.map → comment-reaction-kruNY-kP.mjs.map} +1 -1
  224. package/dist/{comments-CXHuQ6jI.mjs → comments-BUJyXbgq.mjs} +3 -3
  225. package/dist/{comments-CXHuQ6jI.mjs.map → comments-BUJyXbgq.mjs.map} +1 -1
  226. package/dist/{content-B43-YrIq.mjs → content-RmfHoWqK.mjs} +4 -4
  227. package/dist/{content-B43-YrIq.mjs.map → content-RmfHoWqK.mjs.map} +1 -1
  228. package/dist/{context-BW3N7wyt.mjs → context-CzUU_WzL.mjs} +7 -7
  229. package/dist/{context-BW3N7wyt.mjs.map → context-CzUU_WzL.mjs.map} +1 -1
  230. package/dist/{dashboard-FIe0S512.mjs → dashboard-CVyGhift.mjs} +4 -4
  231. package/dist/{dashboard-FIe0S512.mjs.map → dashboard-CVyGhift.mjs.map} +1 -1
  232. package/dist/db/index.d.mts +3 -3
  233. package/dist/db/index.mjs +1 -1
  234. package/dist/db/libsql.d.mts +1 -1
  235. package/dist/db/postgres.d.mts +1 -1
  236. package/dist/db/sqlite.d.mts +1 -1
  237. package/dist/{error-DXnK2wF4.mjs → error-BHkLZk-w.mjs} +2 -2
  238. package/dist/{error-DXnK2wF4.mjs.map → error-BHkLZk-w.mjs.map} +1 -1
  239. package/dist/{field-defs-cache-CYYx8xlG.mjs → field-defs-cache-BA-8ZPiT.mjs} +2 -2
  240. package/dist/{field-defs-cache-CYYx8xlG.mjs.map → field-defs-cache-BA-8ZPiT.mjs.map} +1 -1
  241. package/dist/{fts-manager-DW_NR-Fs.mjs → fts-manager-Db9jGavf.mjs} +2 -2
  242. package/dist/{fts-manager-DW_NR-Fs.mjs.map → fts-manager-Db9jGavf.mjs.map} +1 -1
  243. package/dist/{index-Cz6_JBL3.d.mts → index-CDZZjrLT.d.mts} +2430 -2363
  244. package/dist/index-CDZZjrLT.d.mts.map +1 -0
  245. package/dist/{index-qbL02uhs.d.mts → index-DGIjmUXQ.d.mts} +3 -3
  246. package/dist/{index-qbL02uhs.d.mts.map → index-DGIjmUXQ.d.mts.map} +1 -1
  247. package/dist/index.d.mts +16 -15
  248. package/dist/index.mjs +38 -38
  249. package/dist/{load-DMFi4n1p.mjs → load-DpHICVPh.mjs} +2 -2
  250. package/dist/{load-DMFi4n1p.mjs.map → load-DpHICVPh.mjs.map} +1 -1
  251. package/dist/{loader-DrpofRon.mjs → loader-4K37qA-y.mjs} +81 -18
  252. package/dist/loader-4K37qA-y.mjs.map +1 -0
  253. package/dist/{manifest-schema-BOfPXse0.mjs → manifest-schema-kPGX7VS-.mjs} +3 -1
  254. package/dist/manifest-schema-kPGX7VS-.mjs.map +1 -0
  255. package/dist/media/local-runtime.d.mts +11 -10
  256. package/dist/media/local-runtime.d.mts.map +1 -1
  257. package/dist/media/local-runtime.mjs +5 -5
  258. package/dist/{media-BjKxjzk8.mjs → media-C0nRRfs1.mjs} +2 -2
  259. package/dist/{media-BjKxjzk8.mjs.map → media-C0nRRfs1.mjs.map} +1 -1
  260. package/dist/{menus-CxqJ-z-N.mjs → menus-DzRyC1ID.mjs} +50 -68
  261. package/dist/menus-DzRyC1ID.mjs.map +1 -0
  262. package/dist/{menus-8O6xNcYW.mjs → menus-SL2K0xq2.mjs} +3 -3
  263. package/dist/{menus-8O6xNcYW.mjs.map → menus-SL2K0xq2.mjs.map} +1 -1
  264. package/dist/object-cache/memory.d.mts +1 -1
  265. package/dist/{object-cache-SEb2IM4Z.mjs → object-cache-CHbHv83-.mjs} +8 -4
  266. package/dist/object-cache-CHbHv83-.mjs.map +1 -0
  267. package/dist/{options-BvClpP5Q.d.mts → options-41nCWqi9.d.mts} +2 -2
  268. package/dist/{options-BvClpP5Q.d.mts.map → options-41nCWqi9.d.mts.map} +1 -1
  269. package/dist/page/index.d.mts +6 -3
  270. package/dist/page/index.d.mts.map +1 -1
  271. package/dist/page/index.mjs +3 -2
  272. package/dist/page/index.mjs.map +1 -1
  273. package/dist/{parse-DqMWHQ8x.mjs → parse-CqyZj3Hp.mjs} +2 -2
  274. package/dist/{parse-DqMWHQ8x.mjs.map → parse-CqyZj3Hp.mjs.map} +1 -1
  275. package/dist/plugin-types.d.mts +4 -2
  276. package/dist/plugin-types.d.mts.map +1 -1
  277. package/dist/plugin-utils.d.mts +10 -9
  278. package/dist/plugin-utils.d.mts.map +1 -1
  279. package/dist/plugins/adapt-sandbox-entry.d.mts +10 -9
  280. package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -1
  281. package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
  282. package/dist/{query-34pfm02Q.mjs → query-B0M-9LYR.mjs} +14 -14
  283. package/dist/{query-34pfm02Q.mjs.map → query-B0M-9LYR.mjs.map} +1 -1
  284. package/dist/{rate-limit-CBQK4ygJ.mjs → rate-limit-BwoUdIpC.mjs} +2 -2
  285. package/dist/{rate-limit-CBQK4ygJ.mjs.map → rate-limit-BwoUdIpC.mjs.map} +1 -1
  286. package/dist/{redirect-C1KVGYWe.mjs → redirect-lMOzG04S.mjs} +2 -2
  287. package/dist/{redirect-C1KVGYWe.mjs.map → redirect-lMOzG04S.mjs.map} +1 -1
  288. package/dist/{redirects-DgnypVir.mjs → redirects-9218MmYl.mjs} +3 -3
  289. package/dist/{redirects-DgnypVir.mjs.map → redirects-9218MmYl.mjs.map} +1 -1
  290. package/dist/{registry-3aOAS4My.mjs → registry-DP-iEtdR.mjs} +4 -4
  291. package/dist/{registry-3aOAS4My.mjs.map → registry-DP-iEtdR.mjs.map} +1 -1
  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-CFngB4WZ.mjs} +296 -191
  295. package/dist/runner-CFngB4WZ.mjs.map +1 -0
  296. package/dist/runtime.d.mts +10 -9
  297. package/dist/runtime.d.mts.map +1 -1
  298. package/dist/runtime.mjs +2 -2
  299. package/dist/scheduled-publish-BMljtY5G.d.mts +11 -0
  300. package/dist/scheduled-publish-BMljtY5G.d.mts.map +1 -0
  301. package/dist/{schema-BGwx8AQY.mjs → schema-BfJ7bIXH.mjs} +5 -5
  302. package/dist/{schema-BGwx8AQY.mjs.map → schema-BfJ7bIXH.mjs.map} +1 -1
  303. package/dist/{search-Gl747zMP.mjs → search-C7t3PH1J.mjs} +6 -4
  304. package/dist/search-C7t3PH1J.mjs.map +1 -0
  305. package/dist/{sections-C-umuuLj.mjs → sections-Dm0CVX23.mjs} +3 -3
  306. package/dist/{sections-C-umuuLj.mjs.map → sections-Dm0CVX23.mjs.map} +1 -1
  307. package/dist/seed/index.d.mts +2 -2
  308. package/dist/seed/index.mjs +15 -15
  309. package/dist/{seo-BNzkiK_W.mjs → seo-Dd3tbY_k.mjs} +3 -3
  310. package/dist/{seo-BNzkiK_W.mjs.map → seo-Dd3tbY_k.mjs.map} +1 -1
  311. package/dist/{service-CQHK_10l.mjs → service-CTrqp_Jy.mjs} +2 -2
  312. package/dist/{service-CQHK_10l.mjs.map → service-CTrqp_Jy.mjs.map} +1 -1
  313. package/dist/{settings-C4tXRYiG.mjs → settings-BX8cWBus.mjs} +4 -4
  314. package/dist/{settings-C4tXRYiG.mjs.map → settings-BX8cWBus.mjs.map} +1 -1
  315. package/dist/{settings-fPSR6dJa.mjs → settings-CZ7ZP9cW.mjs} +3 -3
  316. package/dist/{settings-fPSR6dJa.mjs.map → settings-CZ7ZP9cW.mjs.map} +1 -1
  317. package/dist/storage/local.d.mts +1 -1
  318. package/dist/storage/s3.d.mts +1 -1
  319. package/dist/{taxonomies-DbAtL6m4.mjs → taxonomies-DChKcVxf.mjs} +5 -4
  320. package/dist/taxonomies-DChKcVxf.mjs.map +1 -0
  321. package/dist/{taxonomies-n8w72lLQ.mjs → taxonomies-ckPjz-6d.mjs} +75 -20
  322. package/dist/taxonomies-ckPjz-6d.mjs.map +1 -0
  323. package/dist/{taxonomy-BBUlGug5.mjs → taxonomy-Cru-he6s.mjs} +4 -4
  324. package/dist/{taxonomy-BBUlGug5.mjs.map → taxonomy-Cru-he6s.mjs.map} +1 -1
  325. package/dist/{transport-BZ-03aFT.d.mts → transport-Blrl2k_o.d.mts} +1 -1
  326. package/dist/{transport-BZ-03aFT.d.mts.map → transport-Blrl2k_o.d.mts.map} +1 -1
  327. package/dist/{types-h2Cwbzi2.d.mts → types-BYnDBVwe.d.mts} +24 -5
  328. package/dist/{types-h2Cwbzi2.d.mts.map → types-BYnDBVwe.d.mts.map} +1 -1
  329. package/dist/{types-BG8_XTLj.d.mts → types-BkZ8DUEI.d.mts} +1 -1
  330. package/dist/{types-BG8_XTLj.d.mts.map → types-BkZ8DUEI.d.mts.map} +1 -1
  331. package/dist/{types-DrYMm9s5.d.mts → types-ByChcBgE.d.mts} +1 -1
  332. package/dist/{types-DrYMm9s5.d.mts.map → types-ByChcBgE.d.mts.map} +1 -1
  333. package/dist/{types-BWqGVhFn.d.mts → types-CvuKO5Pn.d.mts} +1 -1
  334. package/dist/{types-BWqGVhFn.d.mts.map → types-CvuKO5Pn.d.mts.map} +1 -1
  335. package/dist/{types-CmI1OyQg.mjs → types-DU4TSFS5.mjs} +2 -2
  336. package/dist/{types-CmI1OyQg.mjs.map → types-DU4TSFS5.mjs.map} +1 -1
  337. package/dist/{types-CUsXGRVj.d.mts → types-Dbqff978.d.mts} +1 -1
  338. package/dist/{types-CUsXGRVj.d.mts.map → types-Dbqff978.d.mts.map} +1 -1
  339. package/dist/{types-fS7-VTdR.d.mts → types-Y09-wtyU.d.mts} +1 -1
  340. package/dist/{types-fS7-VTdR.d.mts.map → types-Y09-wtyU.d.mts.map} +1 -1
  341. package/dist/{types-B-gIxFCE.d.mts → types-xIfVRNLp.d.mts} +38 -1
  342. package/dist/types-xIfVRNLp.d.mts.map +1 -0
  343. package/dist/{user-JAuc8VUf.mjs → user-DgGbKL9s.mjs} +3 -3
  344. package/dist/{user-JAuc8VUf.mjs.map → user-DgGbKL9s.mjs.map} +1 -1
  345. package/dist/{validate-Va_An3qb.d.mts → validate-9ECmtEpJ.d.mts} +10 -5
  346. package/dist/{validate-Va_An3qb.d.mts.map → validate-9ECmtEpJ.d.mts.map} +1 -1
  347. package/dist/{validate-BzXHWnkV.mjs → validate-CRkcYZAR.mjs} +2 -2
  348. package/dist/{validate-BzXHWnkV.mjs.map → validate-CRkcYZAR.mjs.map} +1 -1
  349. package/dist/{validation-B6DKPTtU.mjs → validation-sR26SOwD.mjs} +5 -5
  350. package/dist/{validation-B6DKPTtU.mjs.map → validation-sR26SOwD.mjs.map} +1 -1
  351. package/dist/version-CrqhKGWn.mjs +7 -0
  352. package/dist/{version-DHdCA24u.mjs.map → version-CrqhKGWn.mjs.map} +1 -1
  353. package/dist/{widgets-C2RbFN0G.mjs → widgets-DRgtn7Cl.mjs} +2 -2
  354. package/dist/{widgets-C2RbFN0G.mjs.map → widgets-DRgtn7Cl.mjs.map} +1 -1
  355. package/dist/{zod-generator-xEHODMJf.mjs → zod-generator-C5JaV--i.mjs} +6 -3
  356. package/dist/zod-generator-C5JaV--i.mjs.map +1 -0
  357. package/package.json +8 -8
  358. package/src/api/handlers/content.ts +59 -16
  359. package/src/api/handlers/plugins.ts +77 -10
  360. package/src/api/handlers/taxonomies.ts +5 -1
  361. package/src/astro/integration/virtual-modules.ts +15 -0
  362. package/src/astro/integration/vite-config.ts +19 -1
  363. package/src/astro/middleware/auth.ts +1 -0
  364. package/src/astro/middleware.ts +1 -0
  365. package/src/astro/routes/api/admin/plugins/[id]/disable.ts +6 -1
  366. package/src/astro/routes/api/admin/plugins/[id]/enable.ts +6 -1
  367. package/src/astro/routes/api/admin/plugins/[id]/index.ts +1 -0
  368. package/src/astro/routes/api/admin/plugins/index.ts +1 -0
  369. package/src/astro/routes/api/manifest.ts +20 -1
  370. package/src/astro/routes/api/setup/dev-bypass.ts +7 -2
  371. package/src/astro/routes/sitemap-[collection].xml.ts +5 -4
  372. package/src/astro/types.ts +4 -0
  373. package/src/cli/commands/seed.ts +1 -1
  374. package/src/components/EmDashHead.astro +23 -3
  375. package/src/components/JsonLdScript.astro +9 -0
  376. package/src/components/LiveSearch.astro +40 -4
  377. package/src/components/live-search-routing.ts +30 -0
  378. package/src/database/migrations/036_i18n_menus_and_taxonomies.ts +39 -18
  379. package/src/database/migrations/046_media_usage_index.ts +115 -0
  380. package/src/database/migrations/047_restore_taxonomy_parent_index.ts +29 -0
  381. package/src/database/migrations/048_restore_content_taxonomies_term_index.ts +32 -0
  382. package/src/database/migrations/runner.ts +6 -0
  383. package/src/database/repositories/media-usage.ts +444 -0
  384. package/src/database/types.ts +39 -0
  385. package/src/emdash-runtime.ts +61 -52
  386. package/src/index.ts +3 -0
  387. package/src/loader.ts +110 -31
  388. package/src/media/usage/extractor.ts +302 -0
  389. package/src/media/usage/types.ts +46 -0
  390. package/src/object-cache/index.ts +8 -2
  391. package/src/page/metadata.ts +23 -3
  392. package/src/plugin-types.ts +10 -0
  393. package/src/plugins/define-plugin.ts +14 -5
  394. package/src/plugins/hooks.ts +53 -65
  395. package/src/plugins/index.ts +5 -0
  396. package/src/plugins/manager.ts +22 -0
  397. package/src/plugins/manifest-schema.ts +2 -0
  398. package/src/plugins/types.ts +35 -3
  399. package/src/schema/zod-generator.ts +9 -1
  400. package/src/search/query.ts +3 -0
  401. package/src/search/types.ts +2 -0
  402. package/src/seed/apply.ts +8 -2
  403. package/src/seed/types.ts +6 -1
  404. package/src/taxonomies/index.ts +121 -18
  405. package/dist/api-D2_sbavn.mjs.map +0 -1
  406. package/dist/apply-Bk2mAuLn.mjs.map +0 -1
  407. package/dist/index-Cz6_JBL3.d.mts.map +0 -1
  408. package/dist/loader-DrpofRon.mjs.map +0 -1
  409. package/dist/manifest-schema-BOfPXse0.mjs.map +0 -1
  410. package/dist/menus-CxqJ-z-N.mjs.map +0 -1
  411. package/dist/object-cache-SEb2IM4Z.mjs.map +0 -1
  412. package/dist/runner-BsquExlK.mjs.map +0 -1
  413. package/dist/search-Gl747zMP.mjs.map +0 -1
  414. package/dist/taxonomies-DbAtL6m4.mjs.map +0 -1
  415. package/dist/taxonomies-n8w72lLQ.mjs.map +0 -1
  416. package/dist/types-B-gIxFCE.d.mts.map +0 -1
  417. package/dist/version-DHdCA24u.mjs +0 -7
  418. package/dist/zod-generator-xEHODMJf.mjs.map +0 -1
@@ -327,6 +327,36 @@ async function resolveSearchColumns(db: Kysely<Database>, collection: string): P
327
327
  return columns;
328
328
  }
329
329
 
330
+ /**
331
+ * Create a 301 auto-redirect from an entry's old URL to its new one after a
332
+ * slug change, using the collection's URL pattern. Shared by
333
+ * handleContentUpdate (direct slug edits) and handleContentPublish (slug edits
334
+ * staged as `_slug` in a draft revision, which only land on publish).
335
+ */
336
+ async function createSlugChangeRedirect(
337
+ db: Kysely<Database>,
338
+ collection: string,
339
+ oldSlug: string,
340
+ newSlug: string,
341
+ contentId: string,
342
+ ): Promise<void> {
343
+ const collectionRow = await db
344
+ .selectFrom("_emdash_collections")
345
+ .select("url_pattern")
346
+ .where("slug", "=", collection)
347
+ .executeTakeFirst();
348
+
349
+ const redirectRepo = new RedirectRepository(db);
350
+ await redirectRepo.createAutoRedirect(
351
+ collection,
352
+ oldSlug,
353
+ newSlug,
354
+ contentId,
355
+ collectionRow?.url_pattern ?? null,
356
+ );
357
+ invalidateRedirectCache();
358
+ }
359
+
330
360
  /** Matches a date-only `YYYY-MM-DD` bound (no time component). */
331
361
  const DATE_ONLY_RE = /^\d{4}-\d{2}-\d{2}$/;
332
362
 
@@ -865,21 +895,7 @@ export async function handleContentUpdate(
865
895
 
866
896
  // Create auto-redirect when slug changes
867
897
  if (oldSlug && body.slug) {
868
- const collectionRow = await trx
869
- .selectFrom("_emdash_collections")
870
- .select("url_pattern")
871
- .where("slug", "=", collection)
872
- .executeTakeFirst();
873
-
874
- const redirectRepo = new RedirectRepository(trx);
875
- await redirectRepo.createAutoRedirect(
876
- collection,
877
- oldSlug,
878
- body.slug,
879
- resolvedId,
880
- collectionRow?.url_pattern ?? null,
881
- );
882
- invalidateRedirectCache();
898
+ await createSlugChangeRedirect(trx, collection, oldSlug, body.slug, resolvedId);
883
899
  }
884
900
 
885
901
  // Sync non-translatable fields to sibling locales in the same
@@ -1362,7 +1378,34 @@ export async function handleContentPublish(
1362
1378
  const item = await withTransaction(db, async (trx) => {
1363
1379
  const repo = new ContentRepository(trx);
1364
1380
  const resolvedId = (await resolveId(repo, collection, id)) ?? id;
1365
- return repo.publish(collection, resolvedId, options.publishedAt, options.requireScheduledDue);
1381
+
1382
+ // Capture the pre-publish state. For revision-supporting collections a
1383
+ // slug edit is staged as `_slug` in the draft revision and only lands
1384
+ // on the live `slug` column here, inside `repo.publish()` — it never
1385
+ // passes through handleContentUpdate, where slug-change auto-redirects
1386
+ // are normally created.
1387
+ const existing = await repo.findById(collection, resolvedId);
1388
+
1389
+ const published = await repo.publish(
1390
+ collection,
1391
+ resolvedId,
1392
+ options.publishedAt,
1393
+ options.requireScheduledDue,
1394
+ );
1395
+
1396
+ // Leave a 301 behind when publishing changed the slug of an entry that
1397
+ // was already published — its old URL was live and may be indexed or
1398
+ // linked. A first publish is excluded: a draft's URL was never public.
1399
+ if (
1400
+ existing?.status === "published" &&
1401
+ existing.slug &&
1402
+ published.slug &&
1403
+ existing.slug !== published.slug
1404
+ ) {
1405
+ await createSlugChangeRedirect(trx, collection, existing.slug, published.slug, resolvedId);
1406
+ }
1407
+
1408
+ return published;
1366
1409
  });
1367
1410
 
1368
1411
  const hasSeo = await collectionHasSeo(db, collection);
@@ -5,6 +5,7 @@
5
5
  import type { Kysely } from "kysely";
6
6
 
7
7
  import type { Database } from "../../database/types.js";
8
+ import type { SandboxedPluginEntry } from "../../emdash-runtime.js";
8
9
  import { PluginStateRepository, type PluginState, type PluginStatus } from "../../plugins/state.js";
9
10
  import type { ResolvedPlugin } from "../../plugins/types.js";
10
11
  import type { ApiResult } from "../types.js";
@@ -17,6 +18,8 @@ export interface PluginInfo {
17
18
  enabled: boolean;
18
19
  status: PluginStatus;
19
20
  source?: "config" | "marketplace" | "registry";
21
+ /** True for statically-sandboxed plugins (registered via `sandboxed: []`) */
22
+ sandboxed?: boolean;
20
23
  marketplaceVersion?: string;
21
24
  /** Publisher DID, for registry-source plugins */
22
25
  registryPublisherDid?: string;
@@ -84,12 +87,43 @@ function buildPluginInfo(
84
87
  };
85
88
  }
86
89
 
90
+ /**
91
+ * Build plugin info for a statically-sandboxed plugin entry
92
+ */
93
+ function buildSandboxedPluginInfo(
94
+ entry: SandboxedPluginEntry,
95
+ state: PluginState | null,
96
+ ): PluginInfo {
97
+ const status = state?.status ?? "active";
98
+ const enabled = status === "active";
99
+
100
+ return {
101
+ id: entry.id,
102
+ name: state?.displayName || entry.id,
103
+ version: entry.version,
104
+ package: undefined, // v2 doesn't have package field
105
+ enabled,
106
+ status,
107
+ source: "config",
108
+ sandboxed: true,
109
+ capabilities: entry.capabilities,
110
+ hasAdminPages: (entry.adminPages?.length ?? 0) > 0,
111
+ hasDashboardWidgets: (entry.adminWidgets?.length ?? 0) > 0,
112
+ hasHooks: false,
113
+ installedAt: state?.installedAt?.toISOString(),
114
+ activatedAt: state?.activatedAt?.toISOString() ?? undefined,
115
+ deactivatedAt: state?.deactivatedAt?.toISOString() ?? undefined,
116
+ description: state?.description ?? undefined,
117
+ };
118
+ }
119
+
87
120
  /**
88
121
  * List all configured plugins with their state
89
122
  */
90
123
  export async function handlePluginList(
91
124
  db: Kysely<Database>,
92
125
  configuredPlugins: ResolvedPlugin[],
126
+ sandboxedPluginEntries: SandboxedPluginEntry[],
93
127
  marketplaceUrl?: string,
94
128
  ): Promise<ApiResult<PluginListResponse>> {
95
129
  try {
@@ -104,6 +138,14 @@ export async function handlePluginList(
104
138
  return buildPluginInfo(plugin, state, marketplaceUrl);
105
139
  });
106
140
 
141
+ // Include statically-sandboxed plugins (registered via `sandboxed: []`
142
+ // in astro.config.mjs).
143
+ for (const entry of sandboxedPluginEntries) {
144
+ if (configuredIds.has(entry.id)) continue;
145
+ configuredIds.add(entry.id);
146
+ items.push(buildSandboxedPluginInfo(entry, stateMap.get(entry.id) ?? null));
147
+ }
148
+
107
149
  // Include runtime-installed plugins (marketplace or registry) that
108
150
  // aren't in the configured plugins list.
109
151
  for (const state of allStates) {
@@ -156,27 +198,37 @@ export async function handlePluginList(
156
198
  export async function handlePluginGet(
157
199
  db: Kysely<Database>,
158
200
  configuredPlugins: ResolvedPlugin[],
201
+ sandboxedPluginEntries: SandboxedPluginEntry[],
159
202
  pluginId: string,
160
203
  marketplaceUrl?: string,
161
204
  ): Promise<ApiResult<PluginResponse>> {
162
205
  try {
206
+ const stateRepo = new PluginStateRepository(db);
163
207
  const plugin = configuredPlugins.find((p) => p.id === pluginId);
164
- if (!plugin) {
208
+
209
+ if (plugin) {
210
+ const state = await stateRepo.get(pluginId);
165
211
  return {
166
- success: false,
167
- error: {
168
- code: "NOT_FOUND",
169
- message: `Plugin not found: ${pluginId}`,
170
- },
212
+ success: true,
213
+ data: { item: buildPluginInfo(plugin, state, marketplaceUrl) },
171
214
  };
172
215
  }
173
216
 
174
- const stateRepo = new PluginStateRepository(db);
175
- const state = await stateRepo.get(pluginId);
217
+ const sandboxed = sandboxedPluginEntries.find((e) => e.id === pluginId);
218
+ if (sandboxed) {
219
+ const state = await stateRepo.get(pluginId);
220
+ return {
221
+ success: true,
222
+ data: { item: buildSandboxedPluginInfo(sandboxed, state) },
223
+ };
224
+ }
176
225
 
177
226
  return {
178
- success: true,
179
- data: { item: buildPluginInfo(plugin, state, marketplaceUrl) },
227
+ success: false,
228
+ error: {
229
+ code: "NOT_FOUND",
230
+ message: `Plugin not found: ${pluginId}`,
231
+ },
180
232
  };
181
233
  } catch {
182
234
  return {
@@ -227,6 +279,7 @@ function buildStateOnlyPluginInfo(
227
279
  export async function handlePluginEnable(
228
280
  db: Kysely<Database>,
229
281
  configuredPlugins: ResolvedPlugin[],
282
+ sandboxedPluginEntries: SandboxedPluginEntry[],
230
283
  pluginId: string,
231
284
  ): Promise<ApiResult<PluginResponse>> {
232
285
  try {
@@ -239,6 +292,13 @@ export async function handlePluginEnable(
239
292
  return { success: true, data: { item: buildPluginInfo(plugin, state) } };
240
293
  }
241
294
 
295
+ // Statically-sandboxed plugin: addressable via its build-time entry.
296
+ const sandboxed = sandboxedPluginEntries.find((e) => e.id === pluginId);
297
+ if (sandboxed) {
298
+ const state = await stateRepo.enable(pluginId, sandboxed.version);
299
+ return { success: true, data: { item: buildSandboxedPluginInfo(sandboxed, state) } };
300
+ }
301
+
242
302
  // Runtime-installed plugin (marketplace or registry): only
243
303
  // addressable through the state row. Fall back to the existing
244
304
  // version recorded there.
@@ -268,6 +328,7 @@ export async function handlePluginEnable(
268
328
  export async function handlePluginDisable(
269
329
  db: Kysely<Database>,
270
330
  configuredPlugins: ResolvedPlugin[],
331
+ sandboxedPluginEntries: SandboxedPluginEntry[],
271
332
  pluginId: string,
272
333
  ): Promise<ApiResult<PluginResponse>> {
273
334
  try {
@@ -279,6 +340,12 @@ export async function handlePluginDisable(
279
340
  return { success: true, data: { item: buildPluginInfo(plugin, state) } };
280
341
  }
281
342
 
343
+ const sandboxed = sandboxedPluginEntries.find((e) => e.id === pluginId);
344
+ if (sandboxed) {
345
+ const state = await stateRepo.disable(pluginId, sandboxed.version);
346
+ return { success: true, data: { item: buildSandboxedPluginInfo(sandboxed, state) } };
347
+ }
348
+
282
349
  const existing = await stateRepo.get(pluginId);
283
350
  if (!existing || (existing.source !== "marketplace" && existing.source !== "registry")) {
284
351
  return {
@@ -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()
@@ -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;
@@ -444,7 +444,18 @@ export function createViteConfig(
444
444
  // during pre-bundling and can't resolve them. Vite's exclude
445
445
  // uses prefix matching (id.startsWith(m + "/")), so
446
446
  // "virtual:emdash" matches all "virtual:emdash/*" imports.
447
- exclude: ["virtual:emdash"],
447
+ //
448
+ // First-party packages must also stay excluded. In a
449
+ // real install (unlike the workspace symlink, which Vite never
450
+ // optimizes), the optimizer bundles their dist and code-splits
451
+ // lazily-executed dynamic imports (MCP tools, content
452
+ // validation) into hashed chunks. A mid-session re-optimization
453
+ // deletes those chunks while loaded modules still reference
454
+ // them, so every content write fails with "The file does not
455
+ // exist at .../deps_ssr/..." until the dev server restarts.
456
+ // Their CJS deps are still pre-bundled via the "parent > dep"
457
+ // include entries below, which resolve through excluded parents.
458
+ exclude: ["virtual:emdash", "emdash", "@emdash-cms/admin", "@emdash-cms/cloudflare"],
448
459
  include: [
449
460
  // EmDash direct deps
450
461
  "emdash > @portabletext/toolkit",
@@ -455,6 +466,9 @@ export function createViteConfig(
455
466
  "emdash > jose",
456
467
  "emdash > jpeg-js",
457
468
  "emdash > kysely",
469
+ // Only imported by the migration runner, so the first
470
+ // dev-bypass/setup request would otherwise discover it.
471
+ "emdash > kysely/migration",
458
472
  "emdash > mime/lite",
459
473
  "emdash > modern-tar",
460
474
  "emdash > sanitize-html",
@@ -496,6 +510,7 @@ export function createViteConfig(
496
510
  // Top-level deps (use astro > path for pnpm compat)
497
511
  "astro > zod/v4",
498
512
  "astro > zod/v4/core",
513
+ "astro/zod",
499
514
  // zod-generator imports the bare `zod` entry, not `zod/v4`
500
515
  "emdash > zod",
501
516
  "@emdash-cms/cloudflare > kysely-d1",
@@ -507,6 +522,9 @@ export function createViteConfig(
507
522
  "astro/assets/fonts/runtime.js",
508
523
  "astro/assets/services/noop",
509
524
  "@astrojs/cloudflare/image-service",
525
+ // Only imported by the /_image route, so the first image
526
+ // request would otherwise discover it.
527
+ "@astrojs/cloudflare/image-transform-endpoint",
510
528
  ],
511
529
  },
512
530
  }
@@ -120,6 +120,7 @@ const PUBLIC_API_EXACT = new Set([
120
120
  // so unauthenticated callers only see published content. Admin endpoints
121
121
  // (/enable, /rebuild, /stats) remain private because they're not in this set.
122
122
  "/_emdash/api/search",
123
+ "/_emdash/api/search/suggest",
123
124
  ]);
124
125
 
125
126
  // Build merged public routes at module load from auth provider descriptors.
@@ -752,6 +752,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
752
752
  hooks: runtime.hooks,
753
753
  email: runtime.email,
754
754
  configuredPlugins: runtime.configuredPlugins,
755
+ sandboxedPluginEntries: runtime.sandboxedPluginEntries,
755
756
 
756
757
  // Configuration (for checking database type, auth mode, etc.)
757
758
  config,
@@ -28,7 +28,12 @@ export const POST: APIRoute = async ({ params, locals }) => {
28
28
  return apiError("INVALID_REQUEST", "Plugin ID required", 400);
29
29
  }
30
30
 
31
- const result = await handlePluginDisable(emdash.db, emdash.configuredPlugins, id);
31
+ const result = await handlePluginDisable(
32
+ emdash.db,
33
+ emdash.configuredPlugins,
34
+ emdash.sandboxedPluginEntries,
35
+ id,
36
+ );
32
37
 
33
38
  if (!result.success) return unwrapResult(result);
34
39
 
@@ -28,7 +28,12 @@ export const POST: APIRoute = async ({ params, locals }) => {
28
28
  return apiError("INVALID_REQUEST", "Plugin ID required", 400);
29
29
  }
30
30
 
31
- const result = await handlePluginEnable(emdash.db, emdash.configuredPlugins, id);
31
+ const result = await handlePluginEnable(
32
+ emdash.db,
33
+ emdash.configuredPlugins,
34
+ emdash.sandboxedPluginEntries,
35
+ id,
36
+ );
32
37
 
33
38
  if (!result.success) return unwrapResult(result);
34
39
 
@@ -30,6 +30,7 @@ export const GET: APIRoute = async ({ params, locals }) => {
30
30
  const result = await handlePluginGet(
31
31
  emdash.db,
32
32
  emdash.configuredPlugins,
33
+ emdash.sandboxedPluginEntries,
33
34
  id,
34
35
  emdash.config.marketplace,
35
36
  );
@@ -25,6 +25,7 @@ export const GET: APIRoute = async ({ locals }) => {
25
25
  const result = await handlePluginList(
26
26
  emdash.db,
27
27
  emdash.configuredPlugins,
28
+ emdash.sandboxedPluginEntries,
28
29
  emdash.config.marketplace,
29
30
  );
30
31
 
@@ -11,6 +11,7 @@ import type { APIRoute } from "astro";
11
11
 
12
12
  import { handleError } from "#api/error.js";
13
13
  import { getAuthMode } from "#auth/mode.js";
14
+ import { OptionsRepository } from "#db/repositories/options.js";
14
15
 
15
16
  import { COMMIT, VERSION } from "../../../version.js";
16
17
  import type { EmDashManifest } from "../../types.js";
@@ -39,7 +40,25 @@ export const GET: APIRoute = async ({ locals }) => {
39
40
  // doesn't assign it to globalThis, so getStoredConfig() always returned
40
41
  // null and the React SPA never received custom logo/siteName/favicon.
41
42
  // See issue #835.
42
- const adminBranding = emdash?.config?.admin;
43
+ let adminBranding = emdash?.config?.admin;
44
+
45
+ // When no build-time `admin.siteName` is configured, brand the admin with
46
+ // the site's own title so multi-site operators can tell backends apart
47
+ // (WordPress-style: wp-admin always shows the site name). Precedence:
48
+ // explicit `admin.siteName` → Site Title (Settings → General) → the title
49
+ // captured by the setup wizard → the bundled "EmDash" default in the SPA.
50
+ if (!adminBranding?.siteName && emdash?.db) {
51
+ try {
52
+ const options = new OptionsRepository(emdash.db);
53
+ const titles = await options.getMany<string>(["site:title", "emdash:site_title"]);
54
+ const siteTitle = titles.get("site:title") || titles.get("emdash:site_title");
55
+ if (siteTitle) {
56
+ adminBranding = { ...adminBranding, siteName: siteTitle };
57
+ }
58
+ } catch {
59
+ // options table may not exist yet (pre-setup) — keep the default.
60
+ }
61
+ }
43
62
 
44
63
  // Check if self-signup is enabled (any allowed domain with enabled = 1)
45
64
  // Only relevant for passkey auth — external auth providers handle their own signup
@@ -10,6 +10,8 @@
10
10
  * Usage:
11
11
  * - GET with redirect: /_emdash/api/setup/dev-bypass?redirect=/_emdash/admin
12
12
  * - POST for API: Returns JSON with setup info
13
+ * - ?content=0 (or ?content=false): apply schema/structure only, skipping
14
+ * sample content entries, bylines, and taxonomy terms
13
15
  *
14
16
  * For agent/browser testing, navigate to:
15
17
  * /_emdash/api/setup/dev-bypass?redirect=/_emdash/admin
@@ -57,12 +59,15 @@ async function handleDevBypass(context: Parameters<APIRoute>[0]): Promise<Respon
57
59
  const migrations = await runMigrations(emdash.db);
58
60
  console.log("[setup-dev-bypass] Migrations applied:", migrations.applied);
59
61
 
60
- // Apply seed (user seed or built-in default)
62
+ // Apply seed (user seed or built-in default). `?content=0` (or `false`)
63
+ // applies schema/structure only — no sample content, bylines, or terms.
64
+ const contentParam = url.searchParams.get("content");
65
+ const includeContent = contentParam !== "0" && contentParam !== "false";
61
66
  const seed = await loadSeed();
62
67
  const validation = validateSeed(seed);
63
68
  if (validation.valid) {
64
69
  const seedResult = await applySeed(emdash.db, seed, {
65
- includeContent: true,
70
+ includeContent,
66
71
  onConflict: "skip",
67
72
  storage: emdash.storage ?? undefined,
68
73
  });
@@ -138,11 +138,12 @@ export const GET: APIRoute = async ({ params, locals, url }) => {
138
138
  lines.push(" </image:image>");
139
139
  }
140
140
 
141
- if (useXhtml && siblings && siblings.length > 1) {
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 siblings) {
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 && siblings.find((s) => s.locale === i18nConfig.defaultLocale);
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 siblings) {
164
+ for (const sib of alternateEntries) {
164
165
  const sibLoc = await resolveEntryUrl(sib);
165
166
  if (sibLoc !== null) {
166
167
  xDefaultLoc = sibLoc;
@@ -434,6 +434,10 @@ export interface EmDashHandlers {
434
434
  // Configured plugins (for plugin management)
435
435
  configuredPlugins: import("../plugins/types.js").ResolvedPlugin[];
436
436
 
437
+ // Statically-sandboxed plugin entries (registered via `sandboxed: []`),
438
+ // surfaced through the admin plugin management API alongside configured plugins.
439
+ sandboxedPluginEntries: import("../emdash-runtime.js").SandboxedPluginEntry[];
440
+
437
441
  // Configuration (for checking database type, auth mode, etc.)
438
442
  config: import("./integration/runtime.js").EmDashConfig;
439
443
 
@@ -108,7 +108,7 @@ export const seedCommand = defineCommand({
108
108
  },
109
109
  "no-content": {
110
110
  type: "boolean",
111
- description: "Skip sample content",
111
+ description: "Skip sample data (content entries, bylines, taxonomy terms)",
112
112
  default: false,
113
113
  },
114
114
  "on-conflict": {
@@ -17,8 +17,14 @@
17
17
  * ```
18
18
  */
19
19
  import type { PublicPageContext, PageMetadataContribution } from "../plugins/types.js";
20
- import { resolvePageMetadata, renderPageMetadata } from "../page/metadata.js";
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
- metadataHtml = renderPageMetadata(resolved);
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
- metadataHtml = renderPageMetadata(resolved);
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} />
@@ -0,0 +1,9 @@
1
+ ---
2
+ interface Props {
3
+ json: string;
4
+ }
5
+
6
+ const { json } = Astro.props;
7
+ ---
8
+
9
+ <script type="application/ld+json" set:html={json}></script>