emdash 0.17.0 → 0.17.2

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 (290) hide show
  1. package/dist/api/route-utils.mjs +11 -11
  2. package/dist/api/schemas/index.d.mts +1 -1
  3. package/dist/{api-Dmz40c2V.mjs → api-B7GATEYo.mjs} +12 -12
  4. package/dist/{api-Dmz40c2V.mjs.map → api-B7GATEYo.mjs.map} +1 -1
  5. package/dist/{apply-CgamLmed.mjs → apply-BrVqULFe.mjs} +16 -16
  6. package/dist/{apply-CgamLmed.mjs.map → apply-BrVqULFe.mjs.map} +1 -1
  7. package/dist/astro/index.d.mts +2 -2
  8. package/dist/astro/index.mjs +10 -1
  9. package/dist/astro/index.mjs.map +1 -1
  10. package/dist/astro/middleware/auth.d.mts +2 -2
  11. package/dist/astro/middleware/auth.mjs +2 -2
  12. package/dist/astro/middleware/redirect.mjs +5 -5
  13. package/dist/astro/middleware.d.mts.map +1 -1
  14. package/dist/astro/middleware.mjs +65 -49
  15. package/dist/astro/middleware.mjs.map +1 -1
  16. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +3 -3
  17. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +3 -3
  18. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +2 -2
  19. package/dist/astro/routes/api/admin/api-tokens/index.mjs +3 -3
  20. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +3 -3
  21. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +4 -4
  22. package/dist/astro/routes/api/admin/byline-fields/index.mjs +4 -4
  23. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +4 -4
  24. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +9 -9
  25. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +9 -9
  26. package/dist/astro/routes/api/admin/bylines/index.mjs +9 -9
  27. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +7 -7
  28. package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
  29. package/dist/astro/routes/api/admin/comments/bulk.mjs +6 -6
  30. package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
  31. package/dist/astro/routes/api/admin/comments/index.mjs +6 -6
  32. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
  33. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  34. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +3 -3
  35. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +3 -3
  36. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +26 -26
  37. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +26 -26
  38. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +26 -26
  39. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +26 -26
  40. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +26 -26
  41. package/dist/astro/routes/api/admin/plugins/index.mjs +26 -26
  42. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
  43. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +26 -26
  44. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +26 -26
  45. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +26 -26
  46. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +26 -26
  47. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +27 -27
  48. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +26 -26
  49. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +27 -27
  50. package/dist/astro/routes/api/admin/plugins/updates.mjs +26 -26
  51. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +26 -26
  52. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
  53. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +26 -26
  54. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
  55. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
  56. package/dist/astro/routes/api/admin/users/_id_/index.mjs +3 -3
  57. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +2 -2
  58. package/dist/astro/routes/api/admin/users/index.mjs +3 -3
  59. package/dist/astro/routes/api/auth/dev-bypass.mjs +4 -4
  60. package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
  61. package/dist/astro/routes/api/auth/invite/complete.mjs +3 -3
  62. package/dist/astro/routes/api/auth/invite/index.mjs +3 -3
  63. package/dist/astro/routes/api/auth/invite/register-options.mjs +3 -3
  64. package/dist/astro/routes/api/auth/logout.mjs +2 -2
  65. package/dist/astro/routes/api/auth/magic-link/send.mjs +4 -4
  66. package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
  67. package/dist/astro/routes/api/auth/me.mjs +4 -4
  68. package/dist/astro/routes/api/auth/passkey/_id_.mjs +3 -3
  69. package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
  70. package/dist/astro/routes/api/auth/passkey/options.mjs +4 -4
  71. package/dist/astro/routes/api/auth/passkey/register/options.mjs +3 -3
  72. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +3 -3
  73. package/dist/astro/routes/api/auth/passkey/verify.mjs +3 -3
  74. package/dist/astro/routes/api/auth/signup/complete.mjs +3 -3
  75. package/dist/astro/routes/api/auth/signup/request.mjs +4 -4
  76. package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
  77. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +6 -6
  78. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
  79. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
  80. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
  81. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
  82. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +4 -4
  83. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +4 -4
  84. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
  85. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
  86. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +4 -4
  87. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +9 -9
  88. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
  89. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
  90. package/dist/astro/routes/api/content/_collection_/_id_.mjs +4 -4
  91. package/dist/astro/routes/api/content/_collection_/index.mjs +4 -4
  92. package/dist/astro/routes/api/content/_collection_/trash.mjs +4 -4
  93. package/dist/astro/routes/api/dashboard.mjs +7 -7
  94. package/dist/astro/routes/api/dev/emails.mjs +2 -2
  95. package/dist/astro/routes/api/import/probe.mjs +4 -4
  96. package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
  97. package/dist/astro/routes/api/import/wordpress/execute.d.mts +2 -2
  98. package/dist/astro/routes/api/import/wordpress/execute.mjs +8 -8
  99. package/dist/astro/routes/api/import/wordpress/media.mjs +4 -4
  100. package/dist/astro/routes/api/import/wordpress/prepare.mjs +6 -6
  101. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +5 -5
  102. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +4 -4
  103. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +6 -6
  104. package/dist/astro/routes/api/manifest.mjs +3 -3
  105. package/dist/astro/routes/api/mcp.mjs +26 -26
  106. package/dist/astro/routes/api/media/_id_/confirm.mjs +4 -4
  107. package/dist/astro/routes/api/media/_id_.mjs +4 -4
  108. package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
  109. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
  110. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
  111. package/dist/astro/routes/api/media/providers/index.mjs +3 -3
  112. package/dist/astro/routes/api/media/upload-url.mjs +4 -4
  113. package/dist/astro/routes/api/media.mjs +5 -5
  114. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +5 -5
  115. package/dist/astro/routes/api/menus/_name_/items.mjs +5 -5
  116. package/dist/astro/routes/api/menus/_name_/reorder.mjs +5 -5
  117. package/dist/astro/routes/api/menus/_name_/translations.mjs +5 -5
  118. package/dist/astro/routes/api/menus/_name_.mjs +5 -5
  119. package/dist/astro/routes/api/menus/index.mjs +5 -5
  120. package/dist/astro/routes/api/oauth/device/authorize.mjs +3 -3
  121. package/dist/astro/routes/api/oauth/device/code.mjs +4 -4
  122. package/dist/astro/routes/api/oauth/device/token.mjs +4 -4
  123. package/dist/astro/routes/api/oauth/register.mjs +2 -2
  124. package/dist/astro/routes/api/oauth/token/refresh.mjs +3 -3
  125. package/dist/astro/routes/api/oauth/token/revoke.mjs +3 -3
  126. package/dist/astro/routes/api/oauth/token.mjs +2 -2
  127. package/dist/astro/routes/api/openapi.json.mjs +2 -2
  128. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +3 -3
  129. package/dist/astro/routes/api/redirects/404s/index.mjs +7 -7
  130. package/dist/astro/routes/api/redirects/404s/summary.mjs +7 -7
  131. package/dist/astro/routes/api/redirects/_id_.mjs +8 -8
  132. package/dist/astro/routes/api/redirects/index.mjs +8 -8
  133. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
  134. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
  135. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +26 -26
  136. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +26 -26
  137. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +26 -26
  138. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +26 -26
  139. package/dist/astro/routes/api/schema/collections/index.mjs +26 -26
  140. package/dist/astro/routes/api/schema/index.mjs +7 -7
  141. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +26 -26
  142. package/dist/astro/routes/api/schema/orphans/index.mjs +26 -26
  143. package/dist/astro/routes/api/search/enable.mjs +8 -8
  144. package/dist/astro/routes/api/search/index.mjs +7 -7
  145. package/dist/astro/routes/api/search/rebuild.mjs +8 -8
  146. package/dist/astro/routes/api/search/stats.mjs +7 -7
  147. package/dist/astro/routes/api/search/suggest.mjs +7 -7
  148. package/dist/astro/routes/api/sections/_slug_.mjs +7 -7
  149. package/dist/astro/routes/api/sections/index.mjs +7 -7
  150. package/dist/astro/routes/api/settings/email.mjs +4 -4
  151. package/dist/astro/routes/api/settings.mjs +9 -9
  152. package/dist/astro/routes/api/setup/admin-verify.mjs +3 -3
  153. package/dist/astro/routes/api/setup/admin.mjs +3 -3
  154. package/dist/astro/routes/api/setup/dev-bypass.mjs +16 -16
  155. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  156. package/dist/astro/routes/api/setup/index.mjs +17 -17
  157. package/dist/astro/routes/api/setup/status.mjs +3 -3
  158. package/dist/astro/routes/api/snapshot.mjs +3 -3
  159. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +9 -9
  160. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +9 -9
  161. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +9 -9
  162. package/dist/astro/routes/api/taxonomies/index.mjs +9 -9
  163. package/dist/astro/routes/api/themes/preview.mjs +3 -3
  164. package/dist/astro/routes/api/typegen.mjs +5 -5
  165. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +4 -4
  166. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +7 -7
  167. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +7 -7
  168. package/dist/astro/routes/api/widget-areas/_name_.mjs +6 -6
  169. package/dist/astro/routes/api/widget-areas/index.mjs +7 -7
  170. package/dist/astro/routes/api/widget-components.mjs +2 -2
  171. package/dist/astro/routes/robots.txt.mjs +5 -5
  172. package/dist/astro/routes/sitemap-_collection_.xml.mjs +5 -5
  173. package/dist/astro/routes/sitemap.xml.mjs +5 -5
  174. package/dist/astro/types.d.mts +2 -2
  175. package/dist/{authorize-_wWM_44T.mjs → authorize-CLTmOUyx.mjs} +2 -2
  176. package/dist/{authorize-_wWM_44T.mjs.map → authorize-CLTmOUyx.mjs.map} +1 -1
  177. package/dist/{byline-BrIVWLm-.mjs → byline-CAhk4FrG.mjs} +4 -4
  178. package/dist/{byline-BrIVWLm-.mjs.map → byline-CAhk4FrG.mjs.map} +1 -1
  179. package/dist/{byline-fields-BNy7Ng1U.d.mts → byline-fields-CR5hGLMw.d.mts} +28 -28
  180. package/dist/{byline-fields-BNy7Ng1U.d.mts.map → byline-fields-CR5hGLMw.d.mts.map} +1 -1
  181. package/dist/{bylines-sqExMElV.mjs → bylines-CbrD7STW.mjs} +3 -3
  182. package/dist/{bylines-sqExMElV.mjs.map → bylines-CbrD7STW.mjs.map} +1 -1
  183. package/dist/{bylines-C_POWmGT.mjs → bylines-DCczH3AV.mjs} +4 -4
  184. package/dist/{bylines-C_POWmGT.mjs.map → bylines-DCczH3AV.mjs.map} +1 -1
  185. package/dist/{cache-wsDkA8ru.mjs → cache-DIHHyPkt.mjs} +2 -2
  186. package/dist/{cache-wsDkA8ru.mjs.map → cache-DIHHyPkt.mjs.map} +1 -1
  187. package/dist/{chunks-BAYkM-CF.mjs → chunks-DnnHlRG3.mjs} +2 -2
  188. package/dist/{chunks-BAYkM-CF.mjs.map → chunks-DnnHlRG3.mjs.map} +1 -1
  189. package/dist/cli/index.mjs +125 -23
  190. package/dist/cli/index.mjs.map +1 -1
  191. package/dist/{comment-Cd29aktf.mjs → comment-DkAfGX9E.mjs} +2 -2
  192. package/dist/{comment-Cd29aktf.mjs.map → comment-DkAfGX9E.mjs.map} +1 -1
  193. package/dist/{comments-B7ufhkxN.mjs → comments-DLFnXs7J.mjs} +3 -3
  194. package/dist/{comments-B7ufhkxN.mjs.map → comments-DLFnXs7J.mjs.map} +1 -1
  195. package/dist/{content-BbqKo3Kc.mjs → content-C7aJ7keg.mjs} +3 -3
  196. package/dist/{content-BbqKo3Kc.mjs.map → content-C7aJ7keg.mjs.map} +1 -1
  197. package/dist/{context-BsF1rhoI.mjs → context-Ca0HkaIh.mjs} +8 -8
  198. package/dist/{context-BsF1rhoI.mjs.map → context-Ca0HkaIh.mjs.map} +1 -1
  199. package/dist/{dashboard-BwIX9r-X.mjs → dashboard-BrfLIsX1.mjs} +4 -4
  200. package/dist/{dashboard-BwIX9r-X.mjs.map → dashboard-BrfLIsX1.mjs.map} +1 -1
  201. package/dist/db/index.mjs +2 -2
  202. package/dist/{dialect-helpers-BKCvISIQ.mjs → dialect-helpers-DRI5pyY3.mjs} +3 -3
  203. package/dist/dialect-helpers-DRI5pyY3.mjs.map +1 -0
  204. package/dist/{error-npZWBSb7.mjs → error-Bk9s3Ism.mjs} +2 -2
  205. package/dist/{error-npZWBSb7.mjs.map → error-Bk9s3Ism.mjs.map} +1 -1
  206. package/dist/{fts-manager-DmUAk-kQ.mjs → fts-manager-XpDfbIKo.mjs} +3 -3
  207. package/dist/{fts-manager-DmUAk-kQ.mjs.map → fts-manager-XpDfbIKo.mjs.map} +1 -1
  208. package/dist/{index-CjKdMZ3U.d.mts → index-C8ciqSMJ.d.mts} +4 -4
  209. package/dist/{index-CjKdMZ3U.d.mts.map → index-C8ciqSMJ.d.mts.map} +1 -1
  210. package/dist/index.d.mts +2 -2
  211. package/dist/index.mjs +35 -35
  212. package/dist/{load-DsoLq7ex.mjs → load-CF5oETkh.mjs} +2 -2
  213. package/dist/{load-DsoLq7ex.mjs.map → load-CF5oETkh.mjs.map} +1 -1
  214. package/dist/{loader-CJ6lWO0d.mjs → loader-BxyvbrZP.mjs} +4 -4
  215. package/dist/{loader-CJ6lWO0d.mjs.map → loader-BxyvbrZP.mjs.map} +1 -1
  216. package/dist/media/local-runtime.d.mts +2 -2
  217. package/dist/media/local-runtime.mjs +5 -5
  218. package/dist/{media-jk_HzzOl.mjs → media-Cyz5BhSN.mjs} +2 -2
  219. package/dist/{media-jk_HzzOl.mjs.map → media-Cyz5BhSN.mjs.map} +1 -1
  220. package/dist/{menus-CyMO6GBx.mjs → menus-CIdZ_Q6U.mjs} +4 -4
  221. package/dist/{menus-CyMO6GBx.mjs.map → menus-CIdZ_Q6U.mjs.map} +1 -1
  222. package/dist/{menus-B-5-3aon.mjs → menus-PFp8FDuO.mjs} +2 -2
  223. package/dist/{menus-B-5-3aon.mjs.map → menus-PFp8FDuO.mjs.map} +1 -1
  224. package/dist/{parse-4zO5Y2DL.mjs → parse-B-K21lvm.mjs} +2 -2
  225. package/dist/{parse-4zO5Y2DL.mjs.map → parse-B-K21lvm.mjs.map} +1 -1
  226. package/dist/plugin-utils.d.mts +2 -2
  227. package/dist/plugins/adapt-sandbox-entry.d.mts +2 -2
  228. package/dist/{query-CuvjwhrE.mjs → query-Cc649nDl.mjs} +17 -16
  229. package/dist/query-Cc649nDl.mjs.map +1 -0
  230. package/dist/{rate-limit-D6VQqBk_.mjs → rate-limit-BI1OdpQH.mjs} +2 -2
  231. package/dist/{rate-limit-D6VQqBk_.mjs.map → rate-limit-BI1OdpQH.mjs.map} +1 -1
  232. package/dist/{redirect-BZUJltlj.mjs → redirect-C-FeA4j9.mjs} +3 -3
  233. package/dist/{redirect-BZUJltlj.mjs.map → redirect-C-FeA4j9.mjs.map} +1 -1
  234. package/dist/{redirects-DnYuqsEf.mjs → redirects-C1UgU9E0.mjs} +3 -3
  235. package/dist/{redirects-DnYuqsEf.mjs.map → redirects-C1UgU9E0.mjs.map} +1 -1
  236. package/dist/{registry-Dn6gsx3L.mjs → registry-C-T_PWgp.mjs} +5 -5
  237. package/dist/{registry-Dn6gsx3L.mjs.map → registry-C-T_PWgp.mjs.map} +1 -1
  238. package/dist/{runner-eAgyIkeg.mjs → runner-BiuUfx-V.mjs} +4 -4
  239. package/dist/runner-BiuUfx-V.mjs.map +1 -0
  240. package/dist/runtime.d.mts +2 -2
  241. package/dist/runtime.mjs +3 -3
  242. package/dist/{schema--mYZX4D7.mjs → schema-BpCJh2lU.mjs} +4 -4
  243. package/dist/{schema--mYZX4D7.mjs.map → schema-BpCJh2lU.mjs.map} +1 -1
  244. package/dist/{search-C6U_NvZI.mjs → search-BrF7k0Ho.mjs} +4 -4
  245. package/dist/{search-C6U_NvZI.mjs.map → search-BrF7k0Ho.mjs.map} +1 -1
  246. package/dist/{sections-Ba-rJLKb.mjs → sections-8DEa-dWt.mjs} +3 -3
  247. package/dist/{sections-Ba-rJLKb.mjs.map → sections-8DEa-dWt.mjs.map} +1 -1
  248. package/dist/seed/index.mjs +14 -14
  249. package/dist/seo/index.mjs +1 -0
  250. package/dist/seo/index.mjs.map +1 -1
  251. package/dist/{seo-BTzb5ksq.mjs → seo-CKr7pLfA.mjs} +2 -2
  252. package/dist/{seo-BTzb5ksq.mjs.map → seo-CKr7pLfA.mjs.map} +1 -1
  253. package/dist/{service-Cn-kIfZn.mjs → service-9P2cdyR_.mjs} +2 -2
  254. package/dist/{service-Cn-kIfZn.mjs.map → service-9P2cdyR_.mjs.map} +1 -1
  255. package/dist/{settings-C65OSm41.mjs → settings-DYVzINdn.mjs} +3 -3
  256. package/dist/{settings-C65OSm41.mjs.map → settings-DYVzINdn.mjs.map} +1 -1
  257. package/dist/{settings-ChlQbwU0.mjs → settings-Jro4YcUb.mjs} +3 -3
  258. package/dist/{settings-ChlQbwU0.mjs.map → settings-Jro4YcUb.mjs.map} +1 -1
  259. package/dist/{taxonomies-D72gTOg_.mjs → taxonomies-C0bVme_m.mjs} +4 -4
  260. package/dist/{taxonomies-D72gTOg_.mjs.map → taxonomies-C0bVme_m.mjs.map} +1 -1
  261. package/dist/{taxonomies-CgpzAU6F.mjs → taxonomies-CGD6y79Q.mjs} +5 -5
  262. package/dist/{taxonomies-CgpzAU6F.mjs.map → taxonomies-CGD6y79Q.mjs.map} +1 -1
  263. package/dist/{taxonomy-BBK-UAEo.mjs → taxonomy-Db5xwphL.mjs} +3 -3
  264. package/dist/{taxonomy-BBK-UAEo.mjs.map → taxonomy-Db5xwphL.mjs.map} +1 -1
  265. package/dist/{types-SF1DwGf2.mjs → types-CfyYQ7eY.mjs} +2 -2
  266. package/dist/{types-SF1DwGf2.mjs.map → types-CfyYQ7eY.mjs.map} +1 -1
  267. package/dist/{user-X4rtyO4Y.mjs → user-tLdHUEXV.mjs} +2 -2
  268. package/dist/{user-X4rtyO4Y.mjs.map → user-tLdHUEXV.mjs.map} +1 -1
  269. package/dist/{validate-DactmcJG.mjs → validate-DWmnRg6E.mjs} +2 -2
  270. package/dist/{validate-DactmcJG.mjs.map → validate-DWmnRg6E.mjs.map} +1 -1
  271. package/dist/{validation-BYA4i85b.mjs → validation-BQ_TP-On.mjs} +6 -6
  272. package/dist/{validation-BYA4i85b.mjs.map → validation-BQ_TP-On.mjs.map} +1 -1
  273. package/dist/version-CgcnMvqS.mjs +7 -0
  274. package/dist/{version-FGcv0ooe.mjs.map → version-CgcnMvqS.mjs.map} +1 -1
  275. package/dist/{widgets-DG-1jxnz.mjs → widgets-DzlINGI6.mjs} +2 -2
  276. package/dist/{widgets-DG-1jxnz.mjs.map → widgets-DzlINGI6.mjs.map} +1 -1
  277. package/dist/{zod-generator-BNAObjSt.mjs → zod-generator-MMm56Prt.mjs} +2 -2
  278. package/dist/{zod-generator-BNAObjSt.mjs.map → zod-generator-MMm56Prt.mjs.map} +1 -1
  279. package/package.json +6 -6
  280. package/src/astro/integration/vite-config.ts +16 -0
  281. package/src/astro/middleware.ts +34 -8
  282. package/src/cli/commands/export-seed.ts +174 -12
  283. package/src/database/dialect-helpers.ts +8 -2
  284. package/src/database/migrations/019_i18n.ts +2 -2
  285. package/src/query.ts +7 -7
  286. package/src/seo/index.ts +10 -1
  287. package/dist/dialect-helpers-BKCvISIQ.mjs.map +0 -1
  288. package/dist/query-CuvjwhrE.mjs.map +0 -1
  289. package/dist/runner-eAgyIkeg.mjs.map +0 -1
  290. package/dist/version-FGcv0ooe.mjs +0 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emdash",
3
- "version": "0.17.0",
3
+ "version": "0.17.2",
4
4
  "description": "Astro-native CMS with WordPress migration support",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -218,11 +218,11 @@
218
218
  "upng-js": "^2.1.0",
219
219
  "zod": "^4.4.1",
220
220
  "@atcute/client": "^4.2.1",
221
- "@emdash-cms/admin": "0.17.0",
222
- "@emdash-cms/gutenberg-to-portable-text": "0.17.0",
221
+ "@emdash-cms/admin": "0.17.2",
222
+ "@emdash-cms/auth": "0.17.2",
223
+ "@emdash-cms/gutenberg-to-portable-text": "0.17.2",
223
224
  "@emdash-cms/plugin-types": "0.0.1",
224
- "@emdash-cms/registry-client": "0.3.1",
225
- "@emdash-cms/auth": "0.17.0"
225
+ "@emdash-cms/registry-client": "0.3.1"
226
226
  },
227
227
  "optionalDependencies": {
228
228
  "@libsql/kysely-libsql": "^0.4.0",
@@ -255,7 +255,7 @@
255
255
  "vite": "^6.0.0",
256
256
  "vitest": "^4.1.5",
257
257
  "zod-openapi": "^5.4.6",
258
- "@emdash-cms/blocks": "0.17.0"
258
+ "@emdash-cms/blocks": "0.17.2"
259
259
  },
260
260
  "repository": {
261
261
  "type": "git",
@@ -395,10 +395,25 @@ export function createViteConfig(
395
395
  "emdash > @emdash-cms/auth > @oslojs/crypto/ecdsa",
396
396
  "emdash > @emdash-cms/auth > @oslojs/crypto/sha2",
397
397
  "emdash > @emdash-cms/auth > @oslojs/webauthn",
398
+ // Auth deps imported only on auth/login/callback routes, so
399
+ // the initial page scan misses them. Pre-bundle to avoid a
400
+ // re-optimize + reload cascade on first authenticated request.
401
+ "emdash > @oslojs/crypto/hmac",
402
+ "emdash > @oslojs/crypto/subtle",
403
+ "emdash > @oslojs/crypto/rsa",
404
+ "emdash > arctic",
398
405
  // MCP SDK — server/index.js statically imports ajv (CJS-only).
399
406
  // Pre-bundling converts CJS to ESM so workerd can load it.
400
407
  "emdash > @modelcontextprotocol/sdk > ajv",
401
408
  "emdash > @modelcontextprotocol/sdk > ajv-formats",
409
+ // MCP server entrypoints — only imported on the MCP route, so
410
+ // also missed by the initial scan.
411
+ "emdash > @modelcontextprotocol/sdk/server/mcp.js",
412
+ "emdash > @modelcontextprotocol/sdk/server/webStandardStreamableHttp.js",
413
+ // Admin shell SSR deps, reached only when the admin route is
414
+ // first rendered.
415
+ "emdash > @emdash-cms/admin > @lingui/react",
416
+ "emdash > @emdash-cms/admin > @cloudflare/kumo/primitives",
402
417
  // React (commonly used, may be hoisted)
403
418
  "react",
404
419
  "react/jsx-dev-runtime",
@@ -415,6 +430,7 @@ export function createViteConfig(
415
430
  "astro/content/runtime",
416
431
  "astro/assets/utils/inferRemoteSize.js",
417
432
  "astro/assets/fonts/runtime.js",
433
+ "astro/assets/services/noop",
418
434
  "@astrojs/cloudflare/image-service",
419
435
  ],
420
436
  },
@@ -50,6 +50,7 @@ import {
50
50
  type RequestMetrics,
51
51
  runWithContext,
52
52
  } from "../request-context.js";
53
+ import { isMissingTableError } from "../utils/db-errors.js";
53
54
  import type { EmDashConfig } from "./integration/runtime.js";
54
55
  import { createPublicPluginApiRouteHandler } from "./public-plugin-api-routes.js";
55
56
  import type { EmDashHandlers } from "./types.js";
@@ -69,8 +70,24 @@ let i18nInitialized = false;
69
70
  * would query an empty database and crash. Once verified (or once the runtime
70
71
  * has initialized via an admin/API request), this stays true for the worker's
71
72
  * lifetime.
73
+ *
74
+ * Stored on globalThis behind a Symbol key so the flag is a true singleton
75
+ * even when the bundler duplicates this module across SSR chunks (same
76
+ * pattern as request-cache.ts). A plain module-scoped `let` becomes multiple
77
+ * independent variables, which would make the setup probe re-run far more
78
+ * often than intended — and every re-run is another chance for a transient
79
+ * DB error to be misread as "fresh install" and bounce visitors to setup.
72
80
  */
73
- let setupVerified = false;
81
+ const SETUP_VERIFIED_KEY = Symbol.for("emdash:setup-verified");
82
+ const setupFlagStore = globalThis as Record<symbol, unknown>;
83
+
84
+ function isSetupVerified(): boolean {
85
+ return setupFlagStore[SETUP_VERIFIED_KEY] === true;
86
+ }
87
+
88
+ function markSetupVerified(): void {
89
+ setupFlagStore[SETUP_VERIFIED_KEY] = true;
90
+ }
74
91
 
75
92
  /**
76
93
  * Get EmDash configuration from virtual module
@@ -338,7 +355,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
338
355
  // Do a one-time lightweight probe using the same getDb() instance the
339
356
  // page will use: if the migrations table doesn't exist, no migrations
340
357
  // have ever run -- redirect to the setup wizard.
341
- if (!setupVerified) {
358
+ if (!isSetupVerified()) {
342
359
  const t0 = performance.now();
343
360
  try {
344
361
  const { getDb } = await import("../loader.js");
@@ -348,10 +365,19 @@ export const onRequest = defineMiddleware(async (context, next) => {
348
365
  .selectAll()
349
366
  .limit(1)
350
367
  .execute();
351
- setupVerified = true;
352
- } catch {
353
- // Table doesn't exist -> fresh database, redirect to setup
354
- return context.redirect("/_emdash/admin/setup");
368
+ markSetupVerified();
369
+ } catch (error) {
370
+ // Only a genuinely-missing migrations table means a fresh,
371
+ // un-set-up database — redirect to the setup wizard.
372
+ if (isMissingTableError(error)) {
373
+ return context.redirect("/_emdash/admin/setup");
374
+ }
375
+ // Any other failure (transient D1/replica error, timeout, cold-start
376
+ // race, locked SQLite) must NOT be read as "fresh install" — doing so
377
+ // bounces real visitors on a set-up site to /_emdash/admin/setup.
378
+ // Leave the flag unset so a later request can re-verify, and fall
379
+ // through to render the page normally.
380
+ console.error("Setup probe failed (non-fatal):", error);
355
381
  }
356
382
  timings.push({ name: "setup", dur: performance.now() - t0, desc: "Setup probe" });
357
383
  }
@@ -368,7 +394,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
368
394
  const t0 = performance.now();
369
395
  try {
370
396
  const runtime = await getRuntime(config, initSubTimings);
371
- setupVerified = true;
397
+ markSetupVerified();
372
398
  const handlePublicPluginApiRoute = createPublicPluginApiRouteHandler(runtime);
373
399
  // eslint-disable-next-line typescript/no-unsafe-type-assertion -- partial object; getPageRuntime() only checks for the page-contribution methods
374
400
  locals.emdash = {
@@ -448,7 +474,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
448
474
  for (const sub of initSubTimings) timings.push(sub);
449
475
 
450
476
  // Runtime init runs migrations, so the DB is guaranteed set up
451
- setupVerified = true;
477
+ markSetupVerified();
452
478
 
453
479
  // The manifest is no longer pre-loaded here. It's admin-only
454
480
  // content that public/anonymous requests never read, and
@@ -9,14 +9,17 @@ import { resolve } from "node:path";
9
9
  import { defineCommand } from "citty";
10
10
  import consola from "consola";
11
11
  import type { Kysely } from "kysely";
12
+ import { sql } from "kysely";
12
13
 
13
14
  import { createDatabase } from "../../database/connection.js";
14
15
  import { runMigrations } from "../../database/migrations/runner.js";
16
+ import { BylineRepository } from "../../database/repositories/byline.js";
15
17
  import { ContentRepository } from "../../database/repositories/content.js";
16
18
  import { MediaRepository } from "../../database/repositories/media.js";
17
19
  import { OptionsRepository } from "../../database/repositories/options.js";
18
20
  import { TaxonomyRepository } from "../../database/repositories/taxonomy.js";
19
21
  import type { Database } from "../../database/types.js";
22
+ import { validateIdentifier } from "../../database/validate.js";
20
23
  import { isI18nEnabled } from "../../i18n/config.js";
21
24
  import { SchemaRegistry } from "../../schema/registry.js";
22
25
  import type { FieldType } from "../../schema/types.js";
@@ -31,7 +34,10 @@ import type {
31
34
  SeedWidgetArea,
32
35
  SeedWidget,
33
36
  SeedContentEntry,
37
+ SeedByline,
38
+ SeedBylineCredit,
34
39
  } from "../../seed/types.js";
40
+ import { isMissingTableError } from "../../utils/db-errors.js";
35
41
  import { slugify } from "../../utils/slugify.js";
36
42
 
37
43
  const SETTINGS_PREFIX = "site:";
@@ -118,16 +124,29 @@ export async function exportSeed(db: Kysely<Database>, withContent?: string): Pr
118
124
  // 2. Export collections and fields
119
125
  seed.collections = await exportCollections(db);
120
126
 
127
+ // Decide locale-awareness from the data. The runtime sets the i18n config via
128
+ // middleware, but the CLI never does, so `isI18nEnabled()` is always false
129
+ // under `emdash export-seed` (#1330). Detecting multiple locales in the data
130
+ // keeps the export locale-aware without the runtime flag.
131
+ const i18nEnabled = await detectI18nEnabled(db, seed.collections);
132
+
121
133
  // 3. Export taxonomy definitions and terms
122
- seed.taxonomies = await exportTaxonomies(db);
134
+ seed.taxonomies = await exportTaxonomies(db, i18nEnabled);
123
135
 
124
136
  // 4. Export menus
125
- seed.menus = await exportMenus(db);
137
+ seed.menus = await exportMenus(db, i18nEnabled);
126
138
 
127
139
  // 5. Export widget areas
128
140
  seed.widgetAreas = await exportWidgetAreas(db);
129
141
 
130
- // 6. Export content (if requested)
142
+ // 6. Export byline profiles. The returned map (translation_group -> seed-local
143
+ // id) lets content credits below reference the same ids the root list emits.
144
+ const { bylines, groupToSeedId } = await exportBylines(db);
145
+ if (bylines.length > 0) {
146
+ seed.bylines = bylines;
147
+ }
148
+
149
+ // 7. Export content (if requested)
131
150
  if (withContent !== undefined) {
132
151
  const collections =
133
152
  withContent === "" || withContent === "true"
@@ -137,12 +156,112 @@ export async function exportSeed(db: Kysely<Database>, withContent?: string): Pr
137
156
  .map((s) => s.trim())
138
157
  .filter(Boolean);
139
158
 
140
- seed.content = await exportContent(db, seed.collections || [], collections);
159
+ seed.content = await exportContent(
160
+ db,
161
+ seed.collections || [],
162
+ collections,
163
+ groupToSeedId,
164
+ i18nEnabled,
165
+ );
141
166
  }
142
167
 
143
168
  return seed;
144
169
  }
145
170
 
171
+ /**
172
+ * Export byline profiles as root-level `bylines[]`.
173
+ *
174
+ * `SeedByline` has no locale axis, so locale siblings of the same byline
175
+ * (sharing a `translation_group`) collapse to a single profile. The returned
176
+ * `groupToSeedId` map keys on `translation_group` — the value stored in
177
+ * `_emdash_content_bylines.byline_id` — so content credits can resolve to the
178
+ * emitted seed id.
179
+ */
180
+ async function exportBylines(
181
+ db: Kysely<Database>,
182
+ ): Promise<{ bylines: SeedByline[]; groupToSeedId: Map<string, string> }> {
183
+ const bylineRepo = new BylineRepository(db);
184
+ const bylines: SeedByline[] = [];
185
+ const groupToSeedId = new Map<string, string>();
186
+ const usedSeedIds = new Set<string>();
187
+
188
+ let cursor: string | undefined;
189
+ do {
190
+ const result = await bylineRepo.findMany({ limit: 100, cursor });
191
+ for (const byline of result.items) {
192
+ const group = byline.translationGroup ?? byline.id;
193
+ // One seed entry per translation group; first row seen wins.
194
+ if (groupToSeedId.has(group)) continue;
195
+
196
+ let seedId = `byline:${byline.slug}`;
197
+ // Disambiguate the rare case of two distinct groups sharing a slug
198
+ // (slug is unique per-locale, not globally) so seed ids stay unique.
199
+ if (usedSeedIds.has(seedId)) seedId = `byline:${byline.slug}:${group}`;
200
+ usedSeedIds.add(seedId);
201
+ groupToSeedId.set(group, seedId);
202
+
203
+ bylines.push({
204
+ id: seedId,
205
+ slug: byline.slug,
206
+ displayName: byline.displayName,
207
+ bio: byline.bio || undefined,
208
+ websiteUrl: byline.websiteUrl || undefined,
209
+ isGuest: byline.isGuest || undefined,
210
+ });
211
+ }
212
+ cursor = result.nextCursor;
213
+ } while (cursor);
214
+
215
+ return { bylines, groupToSeedId };
216
+ }
217
+
218
+ /**
219
+ * Determine whether the export should emit locale-suffixed seed ids.
220
+ *
221
+ * The runtime initializes the i18n config in middleware, but the CLI never does,
222
+ * so `isI18nEnabled()` is always false under `emdash export-seed` (#1330). When
223
+ * the flag is unset, fall back to the data: a project is multi-locale when its
224
+ * i18n-aware tables hold rows in more than one distinct locale. `locale` is
225
+ * NOT NULL (defaulting to the site's default locale), so a per-row presence
226
+ * check is not enough — only the *count* of distinct locales distinguishes a
227
+ * genuinely single-locale project from a multi-locale one. This keeps
228
+ * single-locale exports on bare ids and gives multi-locale exports the
229
+ * per-locale suffix they need to avoid duplicate seed ids.
230
+ */
231
+ async function detectI18nEnabled(
232
+ db: Kysely<Database>,
233
+ collections: SeedCollection[],
234
+ ): Promise<boolean> {
235
+ if (isI18nEnabled()) return true;
236
+
237
+ const locales = new Set<string>();
238
+ const collectDistinctLocales = async (tableRef: ReturnType<typeof sql.ref>): Promise<boolean> => {
239
+ const result = await sql<{ locale: string | null }>`
240
+ SELECT DISTINCT locale FROM ${tableRef}
241
+ `.execute(db);
242
+ for (const row of result.rows) {
243
+ if (row.locale) locales.add(row.locale);
244
+ }
245
+ return locales.size > 1;
246
+ };
247
+
248
+ if (await collectDistinctLocales(sql.ref("_emdash_taxonomy_defs"))) return true;
249
+ if (await collectDistinctLocales(sql.ref("_emdash_menus"))) return true;
250
+
251
+ for (const collection of collections) {
252
+ validateIdentifier(collection.slug, "collection slug");
253
+ // On D1, deleteCollection is non-atomic, so a collection row can outlive
254
+ // its ec_* table. Skip missing tables rather than crashing the export.
255
+ try {
256
+ if (await collectDistinctLocales(sql.ref(`ec_${collection.slug}`))) return true;
257
+ } catch (error) {
258
+ if (!isMissingTableError(error)) throw error;
259
+ }
260
+ }
261
+
262
+ return false;
263
+ }
264
+
146
265
  /**
147
266
  * Export site settings
148
267
  */
@@ -212,9 +331,10 @@ async function exportCollections(db: Kysely<Database>): Promise<SeedCollection[]
212
331
  /**
213
332
  * Export taxonomy definitions and terms
214
333
  */
215
- async function exportTaxonomies(db: Kysely<Database>): Promise<SeedTaxonomy[]> {
216
- const i18nEnabled = isI18nEnabled();
217
-
334
+ async function exportTaxonomies(
335
+ db: Kysely<Database>,
336
+ i18nEnabled: boolean,
337
+ ): Promise<SeedTaxonomy[]> {
218
338
  // Mirrors the content export pattern: one entry per (name, locale), stable
219
339
  // seed-local id, translations linked via `translationOf` to the anchor's id.
220
340
  const defs = await db
@@ -306,9 +426,7 @@ async function exportTaxonomies(db: Kysely<Database>): Promise<SeedTaxonomy[]> {
306
426
  /**
307
427
  * Export menus with their items
308
428
  */
309
- async function exportMenus(db: Kysely<Database>): Promise<SeedMenu[]> {
310
- const i18nEnabled = isI18nEnabled();
311
-
429
+ async function exportMenus(db: Kysely<Database>, i18nEnabled: boolean): Promise<SeedMenu[]> {
312
430
  const menus = await db
313
431
  .selectFrom("_emdash_menus")
314
432
  .selectAll()
@@ -546,6 +664,8 @@ async function exportContent(
546
664
  db: Kysely<Database>,
547
665
  collections: SeedCollection[],
548
666
  includeCollections: string[] | null,
667
+ bylineGroupToSeedId: Map<string, string>,
668
+ i18nEnabled: boolean,
549
669
  ): Promise<Record<string, SeedContentEntry[]>> {
550
670
  const content: Record<string, SeedContentEntry[]> = {};
551
671
  const contentRepo = new ContentRepository(db);
@@ -579,8 +699,6 @@ async function exportContent(
579
699
  // Media table might not exist or be empty
580
700
  }
581
701
 
582
- const i18nEnabled = isI18nEnabled();
583
-
584
702
  for (const collection of collections) {
585
703
  // Skip if not in include list
586
704
  if (includeCollections && !includeCollections.includes(collection.slug)) {
@@ -642,6 +760,16 @@ async function exportContent(
642
760
  entry.taxonomies = taxonomies;
643
761
  }
644
762
 
763
+ // Get byline credits. Read the junction directly: its `byline_id`
764
+ // stores the translation_group, which is exactly the key in
765
+ // `bylineGroupToSeedId`. This is locale-agnostic (one row per
766
+ // credit) and avoids the locale-sibling fan-out a hydrated read
767
+ // would produce.
768
+ const bylines = await getBylineCredits(db, collection.slug, item.id, bylineGroupToSeedId);
769
+ if (bylines.length > 0) {
770
+ entry.bylines = bylines;
771
+ }
772
+
645
773
  entries.push(entry);
646
774
  }
647
775
 
@@ -723,6 +851,40 @@ function processDataForExport(
723
851
  return result;
724
852
  }
725
853
 
854
+ /**
855
+ * Get ordered byline credits for a content entry as `SeedBylineCredit[]`.
856
+ *
857
+ * The `_emdash_content_bylines.byline_id` column stores the credited byline's
858
+ * `translation_group`, so it maps straight through `groupToSeedId`. Credits
859
+ * whose group wasn't emitted in the root `bylines[]` are skipped (defensive;
860
+ * shouldn't happen for a consistent DB).
861
+ */
862
+ async function getBylineCredits(
863
+ db: Kysely<Database>,
864
+ collection: string,
865
+ entryId: string,
866
+ groupToSeedId: Map<string, string>,
867
+ ): Promise<SeedBylineCredit[]> {
868
+ const rows = await db
869
+ .selectFrom("_emdash_content_bylines")
870
+ .select(["byline_id", "role_label"])
871
+ .where("collection_slug", "=", collection)
872
+ .where("content_id", "=", entryId)
873
+ .orderBy("sort_order", "asc")
874
+ .execute();
875
+
876
+ const credits: SeedBylineCredit[] = [];
877
+ for (const row of rows) {
878
+ const seedId = groupToSeedId.get(row.byline_id);
879
+ if (!seedId) continue;
880
+ const credit: SeedBylineCredit = { byline: seedId };
881
+ if (row.role_label) credit.roleLabel = row.role_label;
882
+ credits.push(credit);
883
+ }
884
+
885
+ return credits;
886
+ }
887
+
726
888
  /**
727
889
  * Get taxonomy term assignments for a content entry
728
890
  */
@@ -74,10 +74,12 @@ export function currentTimestampValue(db: Kysely<any>): RawBuilder<string> {
74
74
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance
75
75
  export async function tableExists(db: Kysely<any>, tableName: string): Promise<boolean> {
76
76
  if (isPostgres(db)) {
77
+ // Scope to the active schema (matches indexExists/columnExists below).
78
+ // Hardcoding 'public' breaks non-public-schema Postgres deployments.
77
79
  const result = await sql<{ exists: boolean }>`
78
80
  SELECT EXISTS(
79
81
  SELECT 1 FROM information_schema.tables
80
- WHERE table_schema = 'public' AND table_name = ${tableName}
82
+ WHERE table_schema = current_schema() AND table_name = ${tableName}
81
83
  ) as exists
82
84
  `.execute(db);
83
85
  return result.rows[0]?.exists === true;
@@ -146,9 +148,13 @@ export async function columnExists(
146
148
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance
147
149
  export async function listTablesLike(db: Kysely<any>, pattern: string): Promise<string[]> {
148
150
  if (isPostgres(db)) {
151
+ // Scope to the connection's active schema rather than hardcoding
152
+ // 'public'. A Postgres deployment using a non-public schema (per-tenant
153
+ // or shared-cluster setups), or per-test schemas, otherwise sees tables
154
+ // from the wrong schema — or none at all. Mirrors migration 038.
149
155
  const result = await sql<{ table_name: string }>`
150
156
  SELECT table_name FROM information_schema.tables
151
- WHERE table_schema = 'public' AND table_name LIKE ${pattern}
157
+ WHERE table_schema = current_schema() AND table_name LIKE ${pattern}
152
158
  `.execute(db);
153
159
  return result.rows.map((r) => r.table_name);
154
160
  }
@@ -142,7 +142,7 @@ async function upPostgres(db: Kysely<unknown>): Promise<void> {
142
142
  const hasLocale = await sql<{ exists: boolean }>`
143
143
  SELECT EXISTS(
144
144
  SELECT 1 FROM information_schema.columns
145
- WHERE table_schema = 'public' AND table_name = ${t} AND column_name = 'locale'
145
+ WHERE table_schema = current_schema() AND table_name = ${t} AND column_name = 'locale'
146
146
  ) as exists
147
147
  `.execute(db);
148
148
  if (hasLocale.rows[0]?.exists === true) continue;
@@ -189,7 +189,7 @@ async function upPostgres(db: Kysely<unknown>): Promise<void> {
189
189
  const hasTranslatable = await sql<{ exists: boolean }>`
190
190
  SELECT EXISTS(
191
191
  SELECT 1 FROM information_schema.columns
192
- WHERE table_schema = 'public' AND table_name = '_emdash_fields' AND column_name = 'translatable'
192
+ WHERE table_schema = current_schema() AND table_name = '_emdash_fields' AND column_name = 'translatable'
193
193
  ) as exists
194
194
  `.execute(db);
195
195
  if (hasTranslatable.rows[0]?.exists !== true) {
package/src/query.ts CHANGED
@@ -495,10 +495,11 @@ async function getEmDashCollectionUncached<T extends string, D = InferCollection
495
495
  const resolvedLocale =
496
496
  filter?.locale ?? ctx?.locale ?? (isI18nEnabled() ? i18nConfig!.defaultLocale : undefined);
497
497
 
498
+ const requestedLimit = filter?.limit;
498
499
  const result = await getLiveCollection(COLLECTION_NAME, {
499
500
  type,
500
501
  status: filter?.status,
501
- limit: filter?.limit,
502
+ limit: requestedLimit && requestedLimit > 0 ? requestedLimit + 1 : filter?.limit,
502
503
  cursor: filter?.cursor,
503
504
  where: filter?.where,
504
505
  orderBy: filter?.orderBy,
@@ -506,18 +507,17 @@ async function getEmDashCollectionUncached<T extends string, D = InferCollection
506
507
  });
507
508
 
508
509
  const { entries, error, cacheHint } = result;
509
- // nextCursor is returned by the emdash loader but not part of Astro's base
510
- // LiveLoader return type. Extract it safely via property descriptor to avoid
511
- // an unsafe type assertion on the `any`-typed result object.
512
- const rawCursor = Object.getOwnPropertyDescriptor(result, "nextCursor")?.value;
513
- const nextCursor: string | undefined = typeof rawCursor === "string" ? rawCursor : undefined;
514
510
 
515
511
  if (error) {
516
512
  return { entries: [], error, cacheHint: {} };
517
513
  }
518
514
 
515
+ const hasMore = requestedLimit != null && requestedLimit > 0 && entries.length > requestedLimit;
516
+ const pageEntries = hasMore ? entries.slice(0, requestedLimit) : entries;
517
+ const nextCursor = hasMore ? encodeEntryCursor(pageEntries.at(-1), filter?.orderBy) : undefined;
518
+
519
519
  const isEditMode = ctx?.editMode ?? false;
520
- const entriesWithEdit = entries.map((entry: ContentEntry<D>) => {
520
+ const entriesWithEdit = pageEntries.map((entry: ContentEntry<D>) => {
521
521
  const dbId = entryDatabaseId(entry);
522
522
  if (isEditMode) {
523
523
  tagEditableFields(entryData(entry), type, dbId);
package/src/seo/index.ts CHANGED
@@ -170,7 +170,16 @@ function buildMediaUrl(imageRef: string, siteUrl?: string): string {
170
170
  return imageRef;
171
171
  }
172
172
 
173
- // Build from media API path
173
+ // Root-relative path the CMS SEO panel stores seo_image as
174
+ // "/_emdash/api/media/file/01KS....svg" (already includes the API
175
+ // prefix). Without this branch we'd re-prefix and produce
176
+ // "${siteUrl}/_emdash/api/media/file//_emdash/api/media/file/<id>"
177
+ // which 404s and breaks <meta property="og:image">.
178
+ if (imageRef.startsWith("/")) {
179
+ return siteUrl ? `${siteUrl.replace(TRAILING_SLASH_RE, "")}${imageRef}` : imageRef;
180
+ }
181
+
182
+ // Bare media_id — build the full media API path
174
183
  const mediaPath = `/_emdash/api/media/file/${imageRef}`;
175
184
  if (siteUrl) {
176
185
  return `${siteUrl.replace(TRAILING_SLASH_RE, "")}${mediaPath}`;
@@ -1 +0,0 @@
1
- {"version":3,"file":"dialect-helpers-BKCvISIQ.mjs","names":[],"sources":["../src/database/dialect-helpers.ts"],"sourcesContent":["/**\n * Dialect-specific SQL helpers\n *\n * Every function takes a Kysely `db` instance and detects the dialect from\n * the adapter class. No module-level state, no globals, no heuristics —\n * the adapter is the source of truth.\n *\n * This is NOT an ORM abstraction — just targeted helpers for the ~15 places\n * that use raw dialect-specific SQL. Most Kysely schema builder code already\n * works cross-dialect.\n */\n\nimport type { ColumnDataType, Kysely, RawBuilder } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport type { DatabaseDialectType } from \"../db/adapters.js\";\nimport { validateIdentifier, validateJsonFieldName } from \"./validate.js\";\n\nexport type { DatabaseDialectType };\n\n/**\n * Detect dialect type from a Kysely instance via the adapter class name.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport function detectDialect(db: Kysely<any>): DatabaseDialectType {\n\tconst name = db.getExecutor().adapter.constructor.name;\n\tif (name === \"PostgresAdapter\") return \"postgres\";\n\treturn \"sqlite\";\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport function isSqlite(db: Kysely<any>): boolean {\n\treturn detectDialect(db) === \"sqlite\";\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport function isPostgres(db: Kysely<any>): boolean {\n\treturn detectDialect(db) === \"postgres\";\n}\n\n/**\n * Default timestamp expression for column defaults.\n * Wrapped in parens for use in CREATE TABLE ... DEFAULT (...).\n *\n * sqlite: (datetime('now'))\n * postgres: CURRENT_TIMESTAMP\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport function currentTimestamp(db: Kysely<any>): RawBuilder<string> {\n\tif (isPostgres(db)) {\n\t\treturn sql`CURRENT_TIMESTAMP`;\n\t}\n\treturn sql`(datetime('now'))`;\n}\n\n/**\n * Timestamp expression for use in WHERE clauses and SET expressions.\n * No wrapping parens.\n *\n * sqlite: datetime('now')\n * postgres: CURRENT_TIMESTAMP\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport function currentTimestampValue(db: Kysely<any>): RawBuilder<string> {\n\tif (isPostgres(db)) {\n\t\treturn sql`CURRENT_TIMESTAMP`;\n\t}\n\treturn sql`datetime('now')`;\n}\n\n/**\n * Check if a table exists in the database.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport async function tableExists(db: Kysely<any>, tableName: string): Promise<boolean> {\n\tif (isPostgres(db)) {\n\t\tconst result = await sql<{ exists: boolean }>`\n\t\t\tSELECT EXISTS(\n\t\t\t\tSELECT 1 FROM information_schema.tables\n\t\t\t\tWHERE table_schema = 'public' AND table_name = ${tableName}\n\t\t\t) as exists\n\t\t`.execute(db);\n\t\treturn result.rows[0]?.exists === true;\n\t}\n\n\tconst result = await sql<{ name: string }>`\n\t\tSELECT name FROM sqlite_master\n\t\tWHERE type = 'table' AND name = ${tableName}\n\t`.execute(db);\n\treturn result.rows.length > 0;\n}\n\n/**\n * Check if an index exists in the database.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport async function indexExists(db: Kysely<any>, indexName: string): Promise<boolean> {\n\tif (isPostgres(db)) {\n\t\tconst result = await sql<{ exists: boolean }>`\n\t\t\tSELECT EXISTS(\n\t\t\t\tSELECT 1 FROM pg_indexes\n\t\t\t\tWHERE schemaname = current_schema() AND indexname = ${indexName}\n\t\t\t) as exists\n\t\t`.execute(db);\n\t\treturn result.rows[0]?.exists === true;\n\t}\n\n\tconst result = await sql<{ name: string }>`\n\t\tSELECT name FROM sqlite_master\n\t\tWHERE type = 'index' AND name = ${indexName}\n\t`.execute(db);\n\treturn result.rows.length > 0;\n}\n\n/**\n * Check if a column exists in the database.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport async function columnExists(\n\tdb: Kysely<any>,\n\ttableName: string,\n\tcolumnName: string,\n): Promise<boolean> {\n\tif (isPostgres(db)) {\n\t\tconst result = await sql<{ exists: boolean }>`\n\t\t\tSELECT EXISTS(\n\t\t\t\tSELECT 1 FROM information_schema.columns\n\t\t\t\tWHERE table_schema = current_schema()\n\t\t\t\t\tAND table_name = ${tableName}\n\t\t\t\t\tAND column_name = ${columnName}\n\t\t\t) as exists\n\t\t`.execute(db);\n\t\treturn result.rows[0]?.exists === true;\n\t}\n\n\tconst result = await sql<{ name: string }>`\n\t\tSELECT name FROM pragma_table_info(${tableName})\n\t\tWHERE name = ${columnName}\n\t`.execute(db);\n\treturn result.rows.length > 0;\n}\n\n/**\n * List tables matching a LIKE pattern.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport async function listTablesLike(db: Kysely<any>, pattern: string): Promise<string[]> {\n\tif (isPostgres(db)) {\n\t\tconst result = await sql<{ table_name: string }>`\n\t\t\tSELECT table_name FROM information_schema.tables\n\t\t\tWHERE table_schema = 'public' AND table_name LIKE ${pattern}\n\t\t`.execute(db);\n\t\treturn result.rows.map((r) => r.table_name);\n\t}\n\n\tconst result = await sql<{ name: string }>`\n\t\tSELECT name FROM sqlite_master\n\t\tWHERE type = 'table' AND name LIKE ${pattern}\n\t`.execute(db);\n\treturn result.rows.map((r) => r.name);\n}\n\n/**\n * Column type for binary data.\n *\n * sqlite: blob\n * postgres: bytea\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport function binaryType(db: Kysely<any>): ColumnDataType {\n\tif (isPostgres(db)) {\n\t\treturn \"bytea\";\n\t}\n\treturn \"blob\";\n}\n\n/**\n * SQL expression for extracting a field from a JSON/JSONB column.\n *\n * sqlite: json_extract(column, '$.path')\n * postgres: column->>'path'\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport function jsonExtractExpr(db: Kysely<any>, column: string, path: string): string {\n\tvalidateIdentifier(column, \"JSON column name\");\n\tvalidateJsonFieldName(path, \"JSON path\");\n\tif (isPostgres(db)) {\n\t\treturn `${column}->>'${path}'`;\n\t}\n\treturn `json_extract(${column}, '$.${path}')`;\n}\n"],"mappings":";;;;;;;AAwBA,SAAgB,cAAc,IAAsC;AAEnE,KADa,GAAG,aAAa,CAAC,QAAQ,YAAY,SACrC,kBAAmB,QAAO;AACvC,QAAO;;AAIR,SAAgB,SAAS,IAA0B;AAClD,QAAO,cAAc,GAAG,KAAK;;AAI9B,SAAgB,WAAW,IAA0B;AACpD,QAAO,cAAc,GAAG,KAAK;;;;;;;;;AAW9B,SAAgB,iBAAiB,IAAqC;AACrE,KAAI,WAAW,GAAG,CACjB,QAAO,GAAG;AAEX,QAAO,GAAG;;;;;;;;;AAWX,SAAgB,sBAAsB,IAAqC;AAC1E,KAAI,WAAW,GAAG,CACjB,QAAO,GAAG;AAEX,QAAO,GAAG;;;;;AAOX,eAAsB,YAAY,IAAiB,WAAqC;AACvF,KAAI,WAAW,GAAG,CAOjB,SANe,MAAM,GAAwB;;;qDAGM,UAAU;;IAE3D,QAAQ,GAAG,EACC,KAAK,IAAI,WAAW;AAOnC,SAJe,MAAM,GAAqB;;oCAEP,UAAU;GAC3C,QAAQ,GAAG,EACC,KAAK,SAAS;;;;;AA6B7B,eAAsB,aACrB,IACA,WACA,YACmB;AACnB,KAAI,WAAW,GAAG,CASjB,SARe,MAAM,GAAwB;;;;wBAIvB,UAAU;yBACT,WAAW;;IAEhC,QAAQ,GAAG,EACC,KAAK,IAAI,WAAW;AAOnC,SAJe,MAAM,GAAqB;uCACJ,UAAU;iBAChC,WAAW;GACzB,QAAQ,GAAG,EACC,KAAK,SAAS;;;;;AAO7B,eAAsB,eAAe,IAAiB,SAAoC;AACzF,KAAI,WAAW,GAAG,CAKjB,SAJe,MAAM,GAA2B;;uDAEK,QAAQ;IAC3D,QAAQ,GAAG,EACC,KAAK,KAAK,MAAM,EAAE,WAAW;AAO5C,SAJe,MAAM,GAAqB;;uCAEJ,QAAQ;GAC5C,QAAQ,GAAG,EACC,KAAK,KAAK,MAAM,EAAE,KAAK;;;;;;;;AAUtC,SAAgB,WAAW,IAAiC;AAC3D,KAAI,WAAW,GAAG,CACjB,QAAO;AAER,QAAO;;;;;;;;AAUR,SAAgB,gBAAgB,IAAiB,QAAgB,MAAsB;AACtF,oBAAmB,QAAQ,mBAAmB;AAC9C,uBAAsB,MAAM,YAAY;AACxC,KAAI,WAAW,GAAG,CACjB,QAAO,GAAG,OAAO,MAAM,KAAK;AAE7B,QAAO,gBAAgB,OAAO,OAAO,KAAK"}