emdash 0.16.1 → 0.17.1

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 (566) hide show
  1. package/dist/{adapters-C4yd_UJR.d.mts → adapters-C5AWLJSD.d.mts} +1 -1
  2. package/dist/{adapters-C4yd_UJR.d.mts.map → adapters-C5AWLJSD.d.mts.map} +1 -1
  3. package/dist/{allowed-origins-D0fFk9a6.mjs → allowed-origins-CyYLEJkp.mjs} +2 -2
  4. package/dist/{allowed-origins-D0fFk9a6.mjs.map → allowed-origins-CyYLEJkp.mjs.map} +1 -1
  5. package/dist/api/route-utils.d.mts +3 -3
  6. package/dist/api/route-utils.mjs +16 -16
  7. package/dist/api/schemas/index.d.mts +2 -2
  8. package/dist/api/schemas/index.mjs +3 -3
  9. package/dist/{api-BNKqxyFX.mjs → api-Dmz40c2V.mjs} +44 -22
  10. package/dist/api-Dmz40c2V.mjs.map +1 -0
  11. package/dist/{api-tokens-ucpcNXDt.mjs → api-tokens-VrXNiNvV.mjs} +2 -2
  12. package/dist/{api-tokens-ucpcNXDt.mjs.map → api-tokens-VrXNiNvV.mjs.map} +1 -1
  13. package/dist/{apply-BOPaD-s9.mjs → apply-CuuZG6op.mjs} +93 -31
  14. package/dist/apply-CuuZG6op.mjs.map +1 -0
  15. package/dist/astro/index.d.mts +10 -10
  16. package/dist/astro/index.mjs +28 -3
  17. package/dist/astro/index.mjs.map +1 -1
  18. package/dist/astro/middleware/auth.d.mts +9 -9
  19. package/dist/astro/middleware/auth.mjs +6 -6
  20. package/dist/astro/middleware/redirect.d.mts.map +1 -1
  21. package/dist/astro/middleware/redirect.mjs +9 -5
  22. package/dist/astro/middleware/redirect.mjs.map +1 -1
  23. package/dist/astro/middleware/request-context.mjs +2 -2
  24. package/dist/astro/middleware/setup.mjs +1 -1
  25. package/dist/astro/middleware.mjs +66 -65
  26. package/dist/astro/middleware.mjs.map +1 -1
  27. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +5 -5
  28. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +5 -5
  29. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
  30. package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
  31. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.d.mts +8 -0
  32. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.d.mts.map +1 -0
  33. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +23 -0
  34. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs.map +1 -0
  35. package/dist/astro/routes/api/admin/byline-fields/_slug_.d.mts +10 -0
  36. package/dist/astro/routes/api/admin/byline-fields/_slug_.d.mts.map +1 -0
  37. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +55 -0
  38. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs.map +1 -0
  39. package/dist/astro/routes/api/admin/byline-fields/index.d.mts +9 -0
  40. package/dist/astro/routes/api/admin/byline-fields/index.d.mts.map +1 -0
  41. package/dist/astro/routes/api/admin/byline-fields/index.mjs +43 -0
  42. package/dist/astro/routes/api/admin/byline-fields/index.mjs.map +1 -0
  43. package/dist/astro/routes/api/admin/byline-fields/reorder.d.mts +8 -0
  44. package/dist/astro/routes/api/admin/byline-fields/reorder.d.mts.map +1 -0
  45. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +27 -0
  46. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs.map +1 -0
  47. package/dist/astro/routes/api/admin/bylines/_id_/index.d.mts.map +1 -1
  48. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +27 -28
  49. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs.map +1 -1
  50. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +13 -12
  51. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs.map +1 -1
  52. package/dist/astro/routes/api/admin/bylines/index.mjs +15 -13
  53. package/dist/astro/routes/api/admin/bylines/index.mjs.map +1 -1
  54. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +10 -10
  55. package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
  56. package/dist/astro/routes/api/admin/comments/bulk.mjs +8 -8
  57. package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
  58. package/dist/astro/routes/api/admin/comments/index.mjs +8 -8
  59. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
  60. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  61. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +4 -4
  62. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
  63. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +35 -34
  64. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  65. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +35 -34
  66. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  67. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +34 -33
  68. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  69. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +34 -33
  70. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  71. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +34 -33
  72. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  73. package/dist/astro/routes/api/admin/plugins/index.mjs +34 -33
  74. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  75. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
  76. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +34 -33
  77. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  78. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +34 -33
  79. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  80. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +34 -33
  81. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  82. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +34 -33
  83. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  84. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +35 -34
  85. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  86. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +34 -33
  87. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  88. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +35 -34
  89. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  90. package/dist/astro/routes/api/admin/plugins/updates.mjs +34 -33
  91. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  92. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +34 -33
  93. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  94. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
  95. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +34 -33
  96. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  97. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
  98. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
  99. package/dist/astro/routes/api/admin/users/_id_/index.mjs +5 -5
  100. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +3 -3
  101. package/dist/astro/routes/api/admin/users/index.mjs +5 -5
  102. package/dist/astro/routes/api/auth/dev-bypass.mjs +5 -5
  103. package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
  104. package/dist/astro/routes/api/auth/invite/complete.mjs +9 -9
  105. package/dist/astro/routes/api/auth/invite/index.mjs +6 -6
  106. package/dist/astro/routes/api/auth/invite/register-options.mjs +8 -8
  107. package/dist/astro/routes/api/auth/logout.mjs +3 -3
  108. package/dist/astro/routes/api/auth/magic-link/send.mjs +8 -8
  109. package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
  110. package/dist/astro/routes/api/auth/me.d.mts.map +1 -1
  111. package/dist/astro/routes/api/auth/me.mjs +18 -11
  112. package/dist/astro/routes/api/auth/me.mjs.map +1 -1
  113. package/dist/astro/routes/api/auth/mode.mjs +1 -1
  114. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
  115. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  116. package/dist/astro/routes/api/auth/passkey/_id_.mjs +5 -5
  117. package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
  118. package/dist/astro/routes/api/auth/passkey/options.mjs +10 -10
  119. package/dist/astro/routes/api/auth/passkey/register/options.mjs +8 -8
  120. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +9 -9
  121. package/dist/astro/routes/api/auth/passkey/verify.mjs +9 -9
  122. package/dist/astro/routes/api/auth/signup/complete.mjs +9 -9
  123. package/dist/astro/routes/api/auth/signup/request.mjs +8 -8
  124. package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
  125. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +11 -11
  126. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
  127. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
  128. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
  129. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
  130. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +9 -9
  131. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +6 -6
  132. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
  133. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
  134. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +6 -6
  135. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.d.mts.map +1 -1
  136. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +18 -13
  137. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs.map +1 -1
  138. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
  139. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
  140. package/dist/astro/routes/api/content/_collection_/_id_.d.mts.map +1 -1
  141. package/dist/astro/routes/api/content/_collection_/_id_.mjs +9 -7
  142. package/dist/astro/routes/api/content/_collection_/_id_.mjs.map +1 -1
  143. package/dist/astro/routes/api/content/_collection_/index.mjs +6 -6
  144. package/dist/astro/routes/api/content/_collection_/trash.mjs +6 -6
  145. package/dist/astro/routes/api/dashboard.mjs +7 -7
  146. package/dist/astro/routes/api/dev/emails.mjs +3 -3
  147. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  148. package/dist/astro/routes/api/import/probe.mjs +10 -10
  149. package/dist/astro/routes/api/import/wordpress/analyze.mjs +4 -4
  150. package/dist/astro/routes/api/import/wordpress/execute.d.mts +9 -9
  151. package/dist/astro/routes/api/import/wordpress/execute.mjs +11 -10
  152. package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
  153. package/dist/astro/routes/api/import/wordpress/media.mjs +8 -8
  154. package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
  155. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +8 -8
  156. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  157. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +10 -10
  158. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +1 -1
  159. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +13 -11
  160. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
  161. package/dist/astro/routes/api/manifest.mjs +4 -4
  162. package/dist/astro/routes/api/mcp.mjs +34 -30
  163. package/dist/astro/routes/api/mcp.mjs.map +1 -1
  164. package/dist/astro/routes/api/media/_id_/confirm.mjs +6 -6
  165. package/dist/astro/routes/api/media/_id_.mjs +6 -6
  166. package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
  167. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
  168. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
  169. package/dist/astro/routes/api/media/providers/index.mjs +3 -3
  170. package/dist/astro/routes/api/media/upload-url.mjs +8 -8
  171. package/dist/astro/routes/api/media.d.mts.map +1 -1
  172. package/dist/astro/routes/api/media.mjs +13 -12
  173. package/dist/astro/routes/api/media.mjs.map +1 -1
  174. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +7 -7
  175. package/dist/astro/routes/api/menus/_name_/items.mjs +7 -7
  176. package/dist/astro/routes/api/menus/_name_/reorder.mjs +7 -7
  177. package/dist/astro/routes/api/menus/_name_/translations.mjs +7 -7
  178. package/dist/astro/routes/api/menus/_name_.mjs +7 -7
  179. package/dist/astro/routes/api/menus/index.mjs +7 -7
  180. package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
  181. package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
  182. package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
  183. package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
  184. package/dist/astro/routes/api/oauth/register.mjs +3 -3
  185. package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
  186. package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
  187. package/dist/astro/routes/api/oauth/token.mjs +6 -6
  188. package/dist/astro/routes/api/openapi.json.mjs +10 -7
  189. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  190. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +4 -4
  191. package/dist/astro/routes/api/redirects/404s/index.mjs +8 -8
  192. package/dist/astro/routes/api/redirects/404s/summary.mjs +8 -8
  193. package/dist/astro/routes/api/redirects/_id_.mjs +9 -9
  194. package/dist/astro/routes/api/redirects/index.mjs +9 -9
  195. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
  196. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
  197. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +34 -33
  198. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  199. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +34 -33
  200. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  201. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +34 -33
  202. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  203. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +34 -33
  204. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  205. package/dist/astro/routes/api/schema/collections/index.mjs +34 -33
  206. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  207. package/dist/astro/routes/api/schema/index.mjs +6 -6
  208. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +34 -33
  209. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  210. package/dist/astro/routes/api/schema/orphans/index.mjs +34 -33
  211. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  212. package/dist/astro/routes/api/search/enable.mjs +9 -9
  213. package/dist/astro/routes/api/search/index.mjs +8 -8
  214. package/dist/astro/routes/api/search/rebuild.mjs +9 -9
  215. package/dist/astro/routes/api/search/stats.mjs +6 -6
  216. package/dist/astro/routes/api/search/suggest.mjs +8 -8
  217. package/dist/astro/routes/api/sections/_slug_.mjs +8 -8
  218. package/dist/astro/routes/api/sections/index.mjs +8 -8
  219. package/dist/astro/routes/api/settings/email.mjs +4 -4
  220. package/dist/astro/routes/api/settings.mjs +11 -11
  221. package/dist/astro/routes/api/setup/admin-verify.mjs +10 -10
  222. package/dist/astro/routes/api/setup/admin.mjs +9 -9
  223. package/dist/astro/routes/api/setup/dev-bypass.mjs +24 -23
  224. package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
  225. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  226. package/dist/astro/routes/api/setup/index.mjs +24 -23
  227. package/dist/astro/routes/api/setup/index.mjs.map +1 -1
  228. package/dist/astro/routes/api/setup/status.mjs +4 -4
  229. package/dist/astro/routes/api/snapshot.mjs +5 -5
  230. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +12 -12
  231. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +12 -12
  232. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +12 -12
  233. package/dist/astro/routes/api/taxonomies/index.mjs +12 -12
  234. package/dist/astro/routes/api/themes/preview.mjs +5 -5
  235. package/dist/astro/routes/api/typegen.mjs +5 -5
  236. package/dist/astro/routes/api/well-known/auth.mjs +1 -1
  237. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  238. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  239. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +6 -6
  240. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +8 -8
  241. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +8 -8
  242. package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
  243. package/dist/astro/routes/api/widget-areas/index.mjs +8 -8
  244. package/dist/astro/routes/api/widget-components.mjs +3 -3
  245. package/dist/astro/routes/robots.txt.mjs +6 -6
  246. package/dist/astro/routes/sitemap-_collection_.xml.mjs +8 -8
  247. package/dist/astro/routes/sitemap.xml.mjs +7 -7
  248. package/dist/astro/types.d.mts +13 -12
  249. package/dist/astro/types.d.mts.map +1 -1
  250. package/dist/auth/providers/github.d.mts +1 -1
  251. package/dist/auth/providers/google.d.mts +1 -1
  252. package/dist/{authorize-Bn4S4DUT.mjs → authorize-_wWM_44T.mjs} +2 -2
  253. package/dist/{authorize-Bn4S4DUT.mjs.map → authorize-_wWM_44T.mjs.map} +1 -1
  254. package/dist/byline-BrIVWLm-.mjs +925 -0
  255. package/dist/byline-BrIVWLm-.mjs.map +1 -0
  256. package/dist/{bylines-DWLnr6-k.d.mts → byline-fields-BNy7Ng1U.d.mts} +151 -23
  257. package/dist/byline-fields-BNy7Ng1U.d.mts.map +1 -0
  258. package/dist/byline-fields-DC3Wkk-U.mjs +123 -0
  259. package/dist/byline-fields-DC3Wkk-U.mjs.map +1 -0
  260. package/dist/byline-fields-Dr-xcb6S.mjs +238 -0
  261. package/dist/byline-fields-Dr-xcb6S.mjs.map +1 -0
  262. package/dist/byline-registry-CxK5g559.mjs +406 -0
  263. package/dist/byline-registry-CxK5g559.mjs.map +1 -0
  264. package/dist/{bylines-n6nykUyI.mjs → bylines-C_POWmGT.mjs} +25 -11
  265. package/dist/{bylines-n6nykUyI.mjs.map → bylines-C_POWmGT.mjs.map} +1 -1
  266. package/dist/bylines-sqExMElV.mjs +204 -0
  267. package/dist/bylines-sqExMElV.mjs.map +1 -0
  268. package/dist/{cache-BcI1yUjR.mjs → cache-wsDkA8ru.mjs} +2 -2
  269. package/dist/{cache-BcI1yUjR.mjs.map → cache-wsDkA8ru.mjs.map} +1 -1
  270. package/dist/{challenge-store-Dng1SxKT.mjs → challenge-store-DGwuCc4R.mjs} +1 -1
  271. package/dist/{challenge-store-Dng1SxKT.mjs.map → challenge-store-DGwuCc4R.mjs.map} +1 -1
  272. package/dist/{chunks-cYG4SnIP.mjs → chunks-BAYkM-CF.mjs} +2 -2
  273. package/dist/{chunks-cYG4SnIP.mjs.map → chunks-BAYkM-CF.mjs.map} +1 -1
  274. package/dist/cli/index.mjs +140 -32
  275. package/dist/cli/index.mjs.map +1 -1
  276. package/dist/client/cf-access.d.mts +1 -1
  277. package/dist/client/index.d.mts +2 -1
  278. package/dist/client/index.d.mts.map +1 -1
  279. package/dist/client/index.mjs +4 -2
  280. package/dist/client/index.mjs.map +1 -1
  281. package/dist/{comment-C76G-9tz.mjs → comment-Cd29aktf.mjs} +2 -2
  282. package/dist/{comment-C76G-9tz.mjs.map → comment-Cd29aktf.mjs.map} +1 -1
  283. package/dist/{comments-CCxFFGY1.mjs → comments-B7ufhkxN.mjs} +3 -3
  284. package/dist/{comments-CCxFFGY1.mjs.map → comments-B7ufhkxN.mjs.map} +1 -1
  285. package/dist/{components-Dx3DM0gg.mjs → components-CTfpu3PZ.mjs} +1 -1
  286. package/dist/{components-Dx3DM0gg.mjs.map → components-CTfpu3PZ.mjs.map} +1 -1
  287. package/dist/{content-8voQNTXX.mjs → content-BbqKo3Kc.mjs} +22 -3
  288. package/dist/content-BbqKo3Kc.mjs.map +1 -0
  289. package/dist/{context-B7qiYrz2.mjs → context-BsF1rhoI.mjs} +9 -9
  290. package/dist/{context-B7qiYrz2.mjs.map → context-BsF1rhoI.mjs.map} +1 -1
  291. package/dist/{cron-Bd3b3iuj.mjs → cron-DZovZUnC.mjs} +1 -1
  292. package/dist/{cron-Bd3b3iuj.mjs.map → cron-DZovZUnC.mjs.map} +1 -1
  293. package/dist/{dashboard-BeaFSPpx.mjs → dashboard-BwIX9r-X.mjs} +4 -4
  294. package/dist/{dashboard-BeaFSPpx.mjs.map → dashboard-BwIX9r-X.mjs.map} +1 -1
  295. package/dist/db/index.d.mts +3 -3
  296. package/dist/db/index.mjs +1 -1
  297. package/dist/db/libsql.d.mts +1 -1
  298. package/dist/db/postgres.d.mts +1 -1
  299. package/dist/db/sqlite.d.mts +1 -1
  300. package/dist/{db-errors-BiYqoX-n.mjs → db-errors-CtzxKBxe.mjs} +1 -1
  301. package/dist/{db-errors-BiYqoX-n.mjs.map → db-errors-CtzxKBxe.mjs.map} +1 -1
  302. package/dist/{default-BvTAYCzx.mjs → default-xLFNSsZ9.mjs} +1 -1
  303. package/dist/{default-BvTAYCzx.mjs.map → default-xLFNSsZ9.mjs.map} +1 -1
  304. package/dist/{device-flow-B9oG8PwP.mjs → device-flow-ptLrVINd.mjs} +4 -4
  305. package/dist/{device-flow-B9oG8PwP.mjs.map → device-flow-ptLrVINd.mjs.map} +1 -1
  306. package/dist/{email-console-CubRll9q.mjs → email-console-DHT2Fbpj.mjs} +1 -1
  307. package/dist/{email-console-CubRll9q.mjs.map → email-console-DHT2Fbpj.mjs.map} +1 -1
  308. package/dist/{error-ChfADBuu.mjs → error-npZWBSb7.mjs} +7 -3
  309. package/dist/error-npZWBSb7.mjs.map +1 -0
  310. package/dist/{escape-Cg6kMELH.mjs → escape-bIyGoW5W.mjs} +1 -1
  311. package/dist/{escape-Cg6kMELH.mjs.map → escape-bIyGoW5W.mjs.map} +1 -1
  312. package/dist/{fts-manager-C_b-4x8u.mjs → fts-manager-DmUAk-kQ.mjs} +2 -2
  313. package/dist/{fts-manager-C_b-4x8u.mjs.map → fts-manager-DmUAk-kQ.mjs.map} +1 -1
  314. package/dist/{hash-DlUxGhQS.mjs → hash-9w3pd3-m.mjs} +1 -1
  315. package/dist/{hash-DlUxGhQS.mjs.map → hash-9w3pd3-m.mjs.map} +1 -1
  316. package/dist/{import-DG80rC_I.mjs → import-Dh8bWmyq.mjs} +3 -3
  317. package/dist/{import-DG80rC_I.mjs.map → import-Dh8bWmyq.mjs.map} +1 -1
  318. package/dist/{index-D_p_jIP1.d.mts → index-CjKdMZ3U.d.mts} +38 -16
  319. package/dist/index-CjKdMZ3U.d.mts.map +1 -0
  320. package/dist/{index-CC42STEm.d.mts → index-D60_SzHG.d.mts} +3 -3
  321. package/dist/{index-CC42STEm.d.mts.map → index-D60_SzHG.d.mts.map} +1 -1
  322. package/dist/index.d.mts +17 -17
  323. package/dist/index.mjs +55 -54
  324. package/dist/{load-CLFRjk9r.mjs → load-DsoLq7ex.mjs} +2 -2
  325. package/dist/{load-CLFRjk9r.mjs.map → load-DsoLq7ex.mjs.map} +1 -1
  326. package/dist/{loader-D-vIJjfY.mjs → loader-CJ6lWO0d.mjs} +75 -19
  327. package/dist/loader-CJ6lWO0d.mjs.map +1 -0
  328. package/dist/{manifest-schema-Czqf0TLu.mjs → manifest-schema-Cj-YrzrF.mjs} +1 -1
  329. package/dist/{manifest-schema-Czqf0TLu.mjs.map → manifest-schema-Cj-YrzrF.mjs.map} +1 -1
  330. package/dist/media/index.d.mts +1 -1
  331. package/dist/media/index.mjs +2 -2
  332. package/dist/media/local-runtime.d.mts +11 -11
  333. package/dist/media/local-runtime.mjs +5 -5
  334. package/dist/{media-allowlist-BNloC69x.mjs → media-allowlist-CMcoYIjQ.mjs} +2 -2
  335. package/dist/{media-allowlist-BNloC69x.mjs.map → media-allowlist-CMcoYIjQ.mjs.map} +1 -1
  336. package/dist/{media-CKQd8AYU.mjs → media-jk_HzzOl.mjs} +7 -2
  337. package/dist/media-jk_HzzOl.mjs.map +1 -0
  338. package/dist/{menus-arUNspyU.mjs → menus-B-5-3aon.mjs} +2 -2
  339. package/dist/{menus-arUNspyU.mjs.map → menus-B-5-3aon.mjs.map} +1 -1
  340. package/dist/{menus-C-nWT5Tu.mjs → menus-CyMO6GBx.mjs} +27 -11
  341. package/dist/menus-CyMO6GBx.mjs.map +1 -0
  342. package/dist/{mime-KV5TqkMN.mjs → mime-CCEzze7W.mjs} +1 -1
  343. package/dist/{mime-KV5TqkMN.mjs.map → mime-CCEzze7W.mjs.map} +1 -1
  344. package/dist/{mode-CaaiebZI.mjs → mode-BjlXswIw.mjs} +1 -1
  345. package/dist/{mode-CaaiebZI.mjs.map → mode-BjlXswIw.mjs.map} +1 -1
  346. package/dist/{normalize-CN5kRSMC.mjs → normalize-DVV8nbrL.mjs} +1 -1
  347. package/dist/{normalize-CN5kRSMC.mjs.map → normalize-DVV8nbrL.mjs.map} +1 -1
  348. package/dist/{oauth-authorization-CTMeVfvj.mjs → oauth-authorization-DvBAL75d.mjs} +4 -4
  349. package/dist/{oauth-authorization-CTMeVfvj.mjs.map → oauth-authorization-DvBAL75d.mjs.map} +1 -1
  350. package/dist/{oauth-clients-eJCbkVSG.mjs → oauth-clients-8mPDStMv.mjs} +1 -1
  351. package/dist/{oauth-clients-eJCbkVSG.mjs.map → oauth-clients-8mPDStMv.mjs.map} +1 -1
  352. package/dist/{oauth-state-store-vOSdOeGe.mjs → oauth-state-store-BJ7YtrfD.mjs} +1 -1
  353. package/dist/{oauth-state-store-vOSdOeGe.mjs.map → oauth-state-store-BJ7YtrfD.mjs.map} +1 -1
  354. package/dist/{oauth-user-lookup-3JwsVw6N.mjs → oauth-user-lookup-BdDSDvjF.mjs} +1 -1
  355. package/dist/{oauth-user-lookup-3JwsVw6N.mjs.map → oauth-user-lookup-BdDSDvjF.mjs.map} +1 -1
  356. package/dist/{options-DhV-gwJb.d.mts → options-tb7DJROi.d.mts} +3 -3
  357. package/dist/{options-DhV-gwJb.d.mts.map → options-tb7DJROi.d.mts.map} +1 -1
  358. package/dist/page/index.d.mts +2 -2
  359. package/dist/{parse-DHbXfvxO.mjs → parse-4zO5Y2DL.mjs} +2 -2
  360. package/dist/{parse-DHbXfvxO.mjs.map → parse-4zO5Y2DL.mjs.map} +1 -1
  361. package/dist/{passkey-config-BloQOT3y.mjs → passkey-config-BDVM86Tj.mjs} +1 -1
  362. package/dist/{passkey-config-BloQOT3y.mjs.map → passkey-config-BDVM86Tj.mjs.map} +1 -1
  363. package/dist/{placeholder-KCkkCtgQ.d.mts → placeholder-B9lUUEmj.d.mts} +1 -1
  364. package/dist/{placeholder-KCkkCtgQ.d.mts.map → placeholder-B9lUUEmj.d.mts.map} +1 -1
  365. package/dist/{placeholder-LqmHqvBw.mjs → placeholder-BZxr8W1j.mjs} +1 -1
  366. package/dist/{placeholder-LqmHqvBw.mjs.map → placeholder-BZxr8W1j.mjs.map} +1 -1
  367. package/dist/plugin-types.d.mts +1 -1
  368. package/dist/plugin-utils.d.mts +9 -9
  369. package/dist/plugins/adapt-sandbox-entry.d.mts +9 -9
  370. package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
  371. package/dist/{preview-D4z0WONU.mjs → preview-BfuRkVKW.mjs} +2 -2
  372. package/dist/{preview-D4z0WONU.mjs.map → preview-BfuRkVKW.mjs.map} +1 -1
  373. package/dist/{public-url-CUWWFME2.mjs → public-url-egRHCy1m.mjs} +1 -1
  374. package/dist/{public-url-CUWWFME2.mjs.map → public-url-egRHCy1m.mjs.map} +1 -1
  375. package/dist/{query-7m6-l0f_.mjs → query-Bt52mHXp.mjs} +19 -18
  376. package/dist/query-Bt52mHXp.mjs.map +1 -0
  377. package/dist/{rate-limit-D8RAXN8b.mjs → rate-limit-D6VQqBk_.mjs} +2 -2
  378. package/dist/{rate-limit-D8RAXN8b.mjs.map → rate-limit-D6VQqBk_.mjs.map} +1 -1
  379. package/dist/{redirect-CjfDGrTd.mjs → redirect-BZUJltlj.mjs} +2 -2
  380. package/dist/{redirect-CjfDGrTd.mjs.map → redirect-BZUJltlj.mjs.map} +1 -1
  381. package/dist/{redirect-BINiRYq4.mjs → redirect-Cw3JTlmj.mjs} +1 -1
  382. package/dist/{redirect-BINiRYq4.mjs.map → redirect-Cw3JTlmj.mjs.map} +1 -1
  383. package/dist/{redirects-COMLwsV5.mjs → redirects-C0L9JUk4.mjs} +19 -6
  384. package/dist/redirects-C0L9JUk4.mjs.map +1 -0
  385. package/dist/{redirects-CowoEHdE.mjs → redirects-DnYuqsEf.mjs} +3 -3
  386. package/dist/{redirects-CowoEHdE.mjs.map → redirects-DnYuqsEf.mjs.map} +1 -1
  387. package/dist/{registry-Cyp-dx6J.mjs → registry-Dn6gsx3L.mjs} +13 -5
  388. package/dist/{registry-Cyp-dx6J.mjs.map → registry-Dn6gsx3L.mjs.map} +1 -1
  389. package/dist/{request-cache-dzCt8TZB.mjs → request-cache-BYMs-BGX.mjs} +23 -2
  390. package/dist/{request-cache-dzCt8TZB.mjs.map → request-cache-BYMs-BGX.mjs.map} +1 -1
  391. package/dist/{request-meta-C_Cjii-T.mjs → request-meta-7ByVLxB-.mjs} +2 -2
  392. package/dist/{request-meta-C_Cjii-T.mjs.map → request-meta-7ByVLxB-.mjs.map} +1 -1
  393. package/dist/{resolve-D6sM-SgF.mjs → resolve-BqYMVG0D.mjs} +1 -1
  394. package/dist/{resolve-D6sM-SgF.mjs.map → resolve-BqYMVG0D.mjs.map} +1 -1
  395. package/dist/{runner-DSQBurMS.d.mts → runner-DM1yR5qd.d.mts} +2 -2
  396. package/dist/{runner-DSQBurMS.d.mts.map → runner-DM1yR5qd.d.mts.map} +1 -1
  397. package/dist/{runner-Drnvs96u.mjs → runner-eAgyIkeg.mjs} +284 -158
  398. package/dist/runner-eAgyIkeg.mjs.map +1 -0
  399. package/dist/runtime.d.mts +10 -10
  400. package/dist/runtime.mjs +2 -2
  401. package/dist/{schema-CI9mYPX3.mjs → schema--mYZX4D7.mjs} +5 -5
  402. package/dist/{schema-CI9mYPX3.mjs.map → schema--mYZX4D7.mjs.map} +1 -1
  403. package/dist/{search-DKz_mGBP.mjs → search-C6U_NvZI.mjs} +4 -4
  404. package/dist/{search-DKz_mGBP.mjs.map → search-C6U_NvZI.mjs.map} +1 -1
  405. package/dist/{secrets-rPdhEBkD.mjs → secrets-YYbTgB1w.mjs} +1 -1
  406. package/dist/{secrets-rPdhEBkD.mjs.map → secrets-YYbTgB1w.mjs.map} +1 -1
  407. package/dist/{sections-DBbCDIAT.mjs → sections-Ba-rJLKb.mjs} +3 -3
  408. package/dist/{sections-DBbCDIAT.mjs.map → sections-Ba-rJLKb.mjs.map} +1 -1
  409. package/dist/seed/index.d.mts +2 -2
  410. package/dist/seed/index.mjs +18 -17
  411. package/dist/seo/index.d.mts +1 -1
  412. package/dist/{seo-BGCyDlkb.mjs → seo-BTzb5ksq.mjs} +2 -2
  413. package/dist/{seo-BGCyDlkb.mjs.map → seo-BTzb5ksq.mjs.map} +1 -1
  414. package/dist/{seo-Dq707mNQ.mjs → seo-DfjLvu8i.mjs} +1 -1
  415. package/dist/{seo-Dq707mNQ.mjs.map → seo-DfjLvu8i.mjs.map} +1 -1
  416. package/dist/{service-B0H7U1Y9.mjs → service-Cn-kIfZn.mjs} +3 -3
  417. package/dist/{service-B0H7U1Y9.mjs.map → service-Cn-kIfZn.mjs.map} +1 -1
  418. package/dist/{settings-DfwNyQkf.mjs → settings-C65OSm41.mjs} +3 -3
  419. package/dist/{settings-DfwNyQkf.mjs.map → settings-C65OSm41.mjs.map} +1 -1
  420. package/dist/{settings-BSXRtTzk.mjs → settings-ChlQbwU0.mjs} +4 -4
  421. package/dist/{settings-BSXRtTzk.mjs.map → settings-ChlQbwU0.mjs.map} +1 -1
  422. package/dist/{setup-complete-MzzN9u0b.mjs → setup-complete-VoEZfasi.mjs} +1 -1
  423. package/dist/{setup-complete-MzzN9u0b.mjs.map → setup-complete-VoEZfasi.mjs.map} +1 -1
  424. package/dist/{setup-nonce-DXuriHsg.mjs → setup-nonce-Bm0uKqmf.mjs} +1 -1
  425. package/dist/{setup-nonce-DXuriHsg.mjs.map → setup-nonce-Bm0uKqmf.mjs.map} +1 -1
  426. package/dist/{site-url-xkhw1tcz.mjs → site-url-Cm8-sJy7.mjs} +1 -1
  427. package/dist/{site-url-xkhw1tcz.mjs.map → site-url-Cm8-sJy7.mjs.map} +1 -1
  428. package/dist/{ssrf-MZ-zrG6-.mjs → ssrf-BsVGIE0Z.mjs} +1 -1
  429. package/dist/{ssrf-MZ-zrG6-.mjs.map → ssrf-BsVGIE0Z.mjs.map} +1 -1
  430. package/dist/storage/local.d.mts +1 -1
  431. package/dist/storage/local.mjs +1 -1
  432. package/dist/storage/s3.d.mts +1 -1
  433. package/dist/storage/s3.mjs +1 -1
  434. package/dist/{taxonomies-CcvrMLbR.mjs → taxonomies-ByLlXrv5.mjs} +8 -8
  435. package/dist/{taxonomies-CcvrMLbR.mjs.map → taxonomies-ByLlXrv5.mjs.map} +1 -1
  436. package/dist/{taxonomies-4vx0nmMr.mjs → taxonomies-CbO6v7EE.mjs} +4 -4
  437. package/dist/{taxonomies-4vx0nmMr.mjs.map → taxonomies-CbO6v7EE.mjs.map} +1 -1
  438. package/dist/{taxonomy-zqGQUqgu.mjs → taxonomy-BBK-UAEo.mjs} +3 -3
  439. package/dist/{taxonomy-zqGQUqgu.mjs.map → taxonomy-BBK-UAEo.mjs.map} +1 -1
  440. package/dist/{tokens-N8otWMmj.mjs → tokens-Bx2afeT-.mjs} +1 -1
  441. package/dist/{tokens-N8otWMmj.mjs.map → tokens-Bx2afeT-.mjs.map} +1 -1
  442. package/dist/{transport-B6CHddbu.mjs → transport--Ck3RBin.mjs} +1 -1
  443. package/dist/{transport-B6CHddbu.mjs.map → transport--Ck3RBin.mjs.map} +1 -1
  444. package/dist/{transport-C2MGqtL6.d.mts → transport-OnMNbsIA.d.mts} +1 -1
  445. package/dist/{transport-C2MGqtL6.d.mts.map → transport-OnMNbsIA.d.mts.map} +1 -1
  446. package/dist/{trusted-proxy-97pajC2f.mjs → trusted-proxy-B4AfnoAp.mjs} +1 -1
  447. package/dist/{trusted-proxy-97pajC2f.mjs.map → trusted-proxy-B4AfnoAp.mjs.map} +1 -1
  448. package/dist/types-D8bhH891.mjs +125 -0
  449. package/dist/{types-DSZl1Dsv.mjs.map → types-D8bhH891.mjs.map} +1 -1
  450. package/dist/{types-DGHWRQgr.d.mts → types-DMwSpvcw.d.mts} +2 -2
  451. package/dist/{types-DGHWRQgr.d.mts.map → types-DMwSpvcw.d.mts.map} +1 -1
  452. package/dist/{types-bYmRn_Uy.d.mts → types-DWnN7weG.d.mts} +1 -1
  453. package/dist/{types-bYmRn_Uy.d.mts.map → types-DWnN7weG.d.mts.map} +1 -1
  454. package/dist/{types-Dgo6y-Ut.d.mts → types-DX6v9KzJ.d.mts} +1 -1
  455. package/dist/{types-Dgo6y-Ut.d.mts.map → types-DX6v9KzJ.d.mts.map} +1 -1
  456. package/dist/{types-DaqNzqVt.d.mts → types-DawhLFwy.d.mts} +35 -1
  457. package/dist/{types-DaqNzqVt.d.mts.map → types-DawhLFwy.d.mts.map} +1 -1
  458. package/dist/{types-CpUuGcd5.d.mts → types-DbCWhHet.d.mts} +8 -2
  459. package/dist/{types-CpUuGcd5.d.mts.map → types-DbCWhHet.d.mts.map} +1 -1
  460. package/dist/{types-Cd9UCu3t.mjs → types-DpFmlNyB.mjs} +1 -1
  461. package/dist/{types-Cd9UCu3t.mjs.map → types-DpFmlNyB.mjs.map} +1 -1
  462. package/dist/{types-D599-ruj.d.mts → types-Qa7-HJJC.d.mts} +1 -1
  463. package/dist/{types-D599-ruj.d.mts.map → types-Qa7-HJJC.d.mts.map} +1 -1
  464. package/dist/{types-B0bmgwMG.mjs → types-SF1DwGf2.mjs} +2 -2
  465. package/dist/types-SF1DwGf2.mjs.map +1 -0
  466. package/dist/{types-DaYDYW6g.d.mts → types-i8_uzhMD.d.mts} +40 -2
  467. package/dist/types-i8_uzhMD.d.mts.map +1 -0
  468. package/dist/{types-CkDSF81F.d.mts → types-kwqCOUxj.d.mts} +1 -1
  469. package/dist/{types-CkDSF81F.d.mts.map → types-kwqCOUxj.d.mts.map} +1 -1
  470. package/dist/{user-hUSOaIJy.mjs → user-X4rtyO4Y.mjs} +2 -2
  471. package/dist/{user-hUSOaIJy.mjs.map → user-X4rtyO4Y.mjs.map} +1 -1
  472. package/dist/{utils-C3wTAP-P.mjs → utils-C4Ih4DML.mjs} +1 -1
  473. package/dist/{utils-C3wTAP-P.mjs.map → utils-C4Ih4DML.mjs.map} +1 -1
  474. package/dist/{validate-IGltez8n.mjs → validate-DactmcJG.mjs} +23 -3
  475. package/dist/validate-DactmcJG.mjs.map +1 -0
  476. package/dist/{validate-DQtHw9NT.d.mts → validate-Dy6nkNls.d.mts} +25 -5
  477. package/dist/{validate-DQtHw9NT.d.mts.map → validate-Dy6nkNls.d.mts.map} +1 -1
  478. package/dist/{validation-Bmymau7y.mjs → validation-BYA4i85b.mjs} +6 -6
  479. package/dist/{validation-Bmymau7y.mjs.map → validation-BYA4i85b.mjs.map} +1 -1
  480. package/dist/version-CWbvq9LG.mjs +7 -0
  481. package/dist/{version-ITD3PlQd.mjs.map → version-CWbvq9LG.mjs.map} +1 -1
  482. package/dist/{widgets-yHQa4c6c.mjs → widgets-DG-1jxnz.mjs} +3 -3
  483. package/dist/{widgets-yHQa4c6c.mjs.map → widgets-DG-1jxnz.mjs.map} +1 -1
  484. package/dist/{zod-generator-B80aap1J.mjs → zod-generator-BNAObjSt.mjs} +3 -3
  485. package/dist/{zod-generator-B80aap1J.mjs.map → zod-generator-BNAObjSt.mjs.map} +1 -1
  486. package/package.json +7 -7
  487. package/src/api/errors.ts +7 -0
  488. package/src/api/handlers/byline-fields.ts +212 -0
  489. package/src/api/handlers/bylines.ts +126 -5
  490. package/src/api/handlers/content.ts +43 -2
  491. package/src/api/handlers/media.ts +2 -0
  492. package/src/api/openapi/document.ts +3 -0
  493. package/src/api/schemas/byline-fields.ts +188 -0
  494. package/src/api/schemas/bylines.ts +42 -0
  495. package/src/api/schemas/content.ts +2 -0
  496. package/src/api/schemas/index.ts +1 -0
  497. package/src/api/schemas/media.ts +2 -0
  498. package/src/astro/integration/routes.ts +27 -0
  499. package/src/astro/integration/vite-config.ts +16 -0
  500. package/src/astro/middleware/redirect.ts +5 -1
  501. package/src/astro/routes/api/admin/byline-fields/[slug]/usage.ts +36 -0
  502. package/src/astro/routes/api/admin/byline-fields/[slug].ts +92 -0
  503. package/src/astro/routes/api/admin/byline-fields/index.ts +66 -0
  504. package/src/astro/routes/api/admin/byline-fields/reorder.ts +39 -0
  505. package/src/astro/routes/api/admin/bylines/[id]/index.ts +23 -21
  506. package/src/astro/routes/api/admin/bylines/index.ts +1 -0
  507. package/src/astro/routes/api/auth/me.ts +21 -10
  508. package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +15 -3
  509. package/src/astro/routes/api/content/[collection]/[id].ts +3 -1
  510. package/src/astro/routes/api/media.ts +1 -0
  511. package/src/astro/types.ts +1 -0
  512. package/src/bylines/field-defs-cache.ts +138 -0
  513. package/src/bylines/index.ts +37 -4
  514. package/src/cli/commands/content.ts +4 -2
  515. package/src/cli/commands/export-seed.ts +174 -12
  516. package/src/client/index.ts +4 -1
  517. package/src/components/InlinePortableTextEditor.tsx +69 -0
  518. package/src/content/converters/portable-text-to-prosemirror.ts +7 -0
  519. package/src/content/converters/prosemirror-to-portable-text.ts +16 -0
  520. package/src/content/converters/types.ts +10 -0
  521. package/src/database/migrations/041_content_locale_list_index.ts +47 -0
  522. package/src/database/migrations/042_byline_fields.ts +157 -0
  523. package/src/database/migrations/runner.ts +4 -0
  524. package/src/database/repositories/byline.ts +758 -50
  525. package/src/database/repositories/content.ts +43 -3
  526. package/src/database/repositories/media.ts +14 -0
  527. package/src/database/repositories/types.ts +38 -0
  528. package/src/database/types.ts +44 -0
  529. package/src/emdash-runtime.ts +4 -1
  530. package/src/index.ts +1 -0
  531. package/src/loader.ts +98 -10
  532. package/src/mcp/server.ts +10 -1
  533. package/src/query.ts +7 -7
  534. package/src/request-cache.ts +23 -0
  535. package/src/schema/byline-registry.ts +671 -0
  536. package/src/schema/registry.ts +14 -0
  537. package/src/schema/types.ts +133 -0
  538. package/src/seed/apply.ts +101 -14
  539. package/src/seed/types.ts +21 -0
  540. package/src/seed/validate.ts +39 -0
  541. package/dist/api-BNKqxyFX.mjs.map +0 -1
  542. package/dist/apply-BOPaD-s9.mjs.map +0 -1
  543. package/dist/byline-BDylH_m4.mjs +0 -404
  544. package/dist/byline-BDylH_m4.mjs.map +0 -1
  545. package/dist/bylines-B7TFEvFf.mjs +0 -118
  546. package/dist/bylines-B7TFEvFf.mjs.map +0 -1
  547. package/dist/bylines-DWLnr6-k.d.mts.map +0 -1
  548. package/dist/content-8voQNTXX.mjs.map +0 -1
  549. package/dist/error-ChfADBuu.mjs.map +0 -1
  550. package/dist/index-D_p_jIP1.d.mts.map +0 -1
  551. package/dist/loader-D-vIJjfY.mjs.map +0 -1
  552. package/dist/media-CKQd8AYU.mjs.map +0 -1
  553. package/dist/menus-C-nWT5Tu.mjs.map +0 -1
  554. package/dist/query-7m6-l0f_.mjs.map +0 -1
  555. package/dist/redirects-COMLwsV5.mjs.map +0 -1
  556. package/dist/runner-Drnvs96u.mjs.map +0 -1
  557. package/dist/setup-Cf_TyOv5.mjs +0 -137
  558. package/dist/setup-Cf_TyOv5.mjs.map +0 -1
  559. package/dist/types-B0bmgwMG.mjs.map +0 -1
  560. package/dist/types-DSZl1Dsv.mjs +0 -83
  561. package/dist/types-DaYDYW6g.d.mts.map +0 -1
  562. package/dist/validate-IGltez8n.mjs.map +0 -1
  563. package/dist/version-ITD3PlQd.mjs +0 -7
  564. /package/dist/{api-tokens-iPIHAY8N.mjs → api-tokens-B6VgoE6M.mjs} +0 -0
  565. /package/dist/{ssrf-BIcd-aXW.mjs → ssrf-BvgVcfNQ.mjs} +0 -0
  566. /package/dist/{types-1NNkmTIn.mjs → types-Cj2S6FuC.mjs} +0 -0
@@ -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
  */
@@ -521,6 +521,7 @@ export class EmDashClient {
521
521
  slug?: string;
522
522
  status?: string;
523
523
  _rev?: string;
524
+ locale?: string;
524
525
  },
525
526
  ): Promise<ContentItem> {
526
527
  // Convert markdown strings to PT
@@ -536,9 +537,11 @@ export class EmDashClient {
536
537
  status: input.status,
537
538
  ...(input._rev ? { _rev: input._rev } : {}),
538
539
  };
540
+ const params = new URLSearchParams();
541
+ if (input.locale) params.set("locale", input.locale);
539
542
  const result = await this.request<{ item: ContentItem; _rev?: string }>(
540
543
  "PUT",
541
- `/content/${encodeURIComponent(collection)}/${encodeURIComponent(id)}`,
544
+ `/content/${encodeURIComponent(collection)}/${encodeURIComponent(id)}${params.toString() ? `?${params}` : ""}`,
542
545
  body,
543
546
  );
544
547
 
@@ -183,6 +183,14 @@ function convertPMNode(node: PMNode): PTBlock | PTBlock[] | null {
183
183
  language: attrStrOpt(node.attrs, "language"),
184
184
  };
185
185
  }
186
+ case "htmlBlock": {
187
+ const rawHtml = node.attrs?.html;
188
+ return {
189
+ _type: "htmlBlock",
190
+ _key: k(),
191
+ html: typeof rawHtml === "string" ? rawHtml : "",
192
+ };
193
+ }
186
194
  case "image": {
187
195
  const provider = attrStrOpt(node.attrs, "provider");
188
196
  return {
@@ -391,6 +399,13 @@ function convertPTBlock(block: PTBlock): JSONContent | null {
391
399
  if (block._type === "break") {
392
400
  return { type: "horizontalRule" };
393
401
  }
402
+ if (block._type === "htmlBlock") {
403
+ const hb = block as PTBlock & { html?: string };
404
+ return {
405
+ type: "htmlBlock",
406
+ attrs: { html: hb.html || "" },
407
+ };
408
+ }
394
409
  if (block._type === "image") {
395
410
  const ib = block as PTBlock & {
396
411
  asset?: { _ref?: string; url?: string; provider?: string };
@@ -731,6 +746,21 @@ const slashCommands: SlashCommandItem[] = [
731
746
  editor.chain().focus().deleteRange(range).toggleCodeBlock().run();
732
747
  },
733
748
  },
749
+ {
750
+ id: "htmlBlock",
751
+ title: "HTML",
752
+ description: "Insert raw HTML",
753
+ icon: "< >",
754
+ aliases: ["html", "raw", "markup"],
755
+ command: ({ editor, range }) => {
756
+ editor
757
+ .chain()
758
+ .focus()
759
+ .deleteRange(range)
760
+ .insertContent({ type: "htmlBlock", attrs: { html: "" } })
761
+ .run();
762
+ },
763
+ },
734
764
  {
735
765
  id: "divider",
736
766
  title: "Divider",
@@ -771,6 +801,44 @@ const initialSlashMenuState: SlashMenuState = {
771
801
  range: null,
772
802
  };
773
803
 
804
+ /**
805
+ * Minimal `htmlBlock` TipTap node for the inline (visual-editing) editor.
806
+ *
807
+ * Like PluginBlockNode below, this renders as a read-only placeholder so the
808
+ * block's data is preserved across edits in the visual editor. Full editing
809
+ * (textarea + preview) is only available in the admin editor.
810
+ */
811
+ const HtmlBlockNode = Node.create({
812
+ name: "htmlBlock",
813
+ group: "block",
814
+ atom: true,
815
+ selectable: true,
816
+ draggable: true,
817
+
818
+ addAttributes() {
819
+ const noDom = { rendered: false, parseHTML: () => null };
820
+ return {
821
+ html: { default: "", ...noDom },
822
+ };
823
+ },
824
+
825
+ parseHTML() {
826
+ return [{ tag: 'div[data-emdash-html-block="true"]' }];
827
+ },
828
+
829
+ renderHTML({ HTMLAttributes }) {
830
+ return [
831
+ "div",
832
+ mergeAttributes(HTMLAttributes, {
833
+ "data-emdash-html-block": "true",
834
+ class: "emdash-plugin-block-placeholder",
835
+ contenteditable: "false",
836
+ }),
837
+ "HTML block (edit in admin)",
838
+ ];
839
+ },
840
+ });
841
+
774
842
  /**
775
843
  * Minimal `pluginBlock` TipTap node for the inline (visual-editing) editor.
776
844
  *
@@ -1801,6 +1869,7 @@ export function InlinePortableTextEditor({
1801
1869
  mode: "all",
1802
1870
  }),
1803
1871
  Typography,
1872
+ HtmlBlockNode,
1804
1873
  PluginBlockNode,
1805
1874
  slashCommandsExtension,
1806
1875
  ],
@@ -121,6 +121,13 @@ function convertBlock(block: PortableTextBlock): ProseMirrorNode | null {
121
121
  if (isCodeBlock(block)) {
122
122
  return convertCodeBlock(block);
123
123
  }
124
+ if (block._type === "htmlBlock") {
125
+ const hb = block as PortableTextBlock & { html?: string };
126
+ return {
127
+ type: "htmlBlock",
128
+ attrs: { html: hb.html || "" },
129
+ };
130
+ }
124
131
  if (block._type === "break") {
125
132
  return { type: "horizontalRule" };
126
133
  }
@@ -14,6 +14,7 @@ import type {
14
14
  PortableTextMarkDef,
15
15
  PortableTextImageBlock,
16
16
  PortableTextCodeBlock,
17
+ PortableTextHtmlBlock,
17
18
  } from "./types.js";
18
19
 
19
20
  /**
@@ -70,6 +71,9 @@ function convertNode(node: ProseMirrorNode): PortableTextBlock | PortableTextBlo
70
71
  case "codeBlock":
71
72
  return convertCodeBlock(node);
72
73
 
74
+ case "htmlBlock":
75
+ return convertHtmlBlock(node);
76
+
73
77
  case "image":
74
78
  return convertImage(node);
75
79
 
@@ -268,6 +272,18 @@ function convertCodeBlock(node: ProseMirrorNode): PortableTextCodeBlock {
268
272
  };
269
273
  }
270
274
 
275
+ /**
276
+ * Convert HTML block to Portable Text
277
+ */
278
+ function convertHtmlBlock(node: ProseMirrorNode): PortableTextHtmlBlock {
279
+ const rawHtml = node.attrs?.html;
280
+ return {
281
+ _type: "htmlBlock",
282
+ _key: generateKey(),
283
+ html: typeof rawHtml === "string" ? rawHtml : "",
284
+ };
285
+ }
286
+
271
287
  /**
272
288
  * Convert image to Portable Text
273
289
  */
@@ -80,6 +80,15 @@ export interface PortableTextCodeBlock {
80
80
  filename?: string;
81
81
  }
82
82
 
83
+ /**
84
+ * HTML block (raw HTML content)
85
+ */
86
+ export interface PortableTextHtmlBlock {
87
+ _type: "htmlBlock";
88
+ _key: string;
89
+ html: string;
90
+ }
91
+
83
92
  /**
84
93
  * Unknown/custom block (preserved for plugin compatibility)
85
94
  */
@@ -96,6 +105,7 @@ export type PortableTextBlock =
96
105
  | PortableTextTextBlock
97
106
  | PortableTextImageBlock
98
107
  | PortableTextCodeBlock
108
+ | PortableTextHtmlBlock
99
109
  | PortableTextUnknownBlock;
100
110
 
101
111
  /**
@@ -0,0 +1,47 @@
1
+ import type { Kysely } from "kysely";
2
+ import { sql } from "kysely";
3
+
4
+ import { listTablesLike } from "../dialect-helpers.js";
5
+
6
+ /**
7
+ * Migration: locale-aware composite indexes for content list queries.
8
+ *
9
+ * Addresses GitHub issue #1219. When i18n is enabled the admin content list
10
+ * filters by `locale` and orders by `updated_at`/`created_at`. The existing
11
+ * composite indexes (033/034) cover `(deleted_at, updated_at DESC, id DESC)`
12
+ * etc. but omit `locale`, so a locale-filtered ordered list can't be served
13
+ * by a single index on large tables. These indexes restore index-only paging
14
+ * for the locale-scoped case.
15
+ *
16
+ * Forward-only and idempotent (`IF NOT EXISTS`).
17
+ *
18
+ * Index names use a short `loc_upd`/`loc_crt` suffix rather than spelling out
19
+ * `deleted_locale_updated_id`: Postgres truncates identifiers to 63 bytes, and
20
+ * the longer form pushes the `updated`/`created` discriminator past byte 63 for
21
+ * slugs as short as 40 chars, making both names truncate to the same string.
22
+ * Keep these identical to the names in `schema/registry.ts`.
23
+ */
24
+ export async function up(db: Kysely<unknown>): Promise<void> {
25
+ const tableNames = await listTablesLike(db, "ec_%");
26
+
27
+ for (const tableName of tableNames) {
28
+ await sql`
29
+ CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${tableName}_loc_upd`)}
30
+ ON ${sql.ref(tableName)} (deleted_at, locale, updated_at DESC, id DESC)
31
+ `.execute(db);
32
+
33
+ await sql`
34
+ CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${tableName}_loc_crt`)}
35
+ ON ${sql.ref(tableName)} (deleted_at, locale, created_at DESC, id DESC)
36
+ `.execute(db);
37
+ }
38
+ }
39
+
40
+ export async function down(db: Kysely<unknown>): Promise<void> {
41
+ const tableNames = await listTablesLike(db, "ec_%");
42
+
43
+ for (const tableName of tableNames) {
44
+ await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${tableName}_loc_upd`)}`.execute(db);
45
+ await sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${tableName}_loc_crt`)}`.execute(db);
46
+ }
47
+ }
@@ -0,0 +1,157 @@
1
+ import type { Kysely } from "kysely";
2
+ import { sql } from "kysely";
3
+
4
+ import { currentTimestamp } from "../dialect-helpers.js";
5
+
6
+ /**
7
+ * Byline custom fields (Discussion #1174). Adds three tables and a
8
+ * version-counter row in `options`. Purely additive: no change to
9
+ * `_emdash_bylines` columns landed by migration 040, no change to
10
+ * `_emdash_content_bylines`.
11
+ *
12
+ * Storage model (D11 in the design spec):
13
+ *
14
+ * - `_emdash_byline_fields` — definitions. One row per registered field.
15
+ * - `_emdash_byline_field_values` — translatable values, keyed by
16
+ * `(byline_id, field_id)`. One value per locale variant of a byline.
17
+ * - `_emdash_byline_field_group_values` — non-translatable values, keyed
18
+ * by `(translation_group, field_id)`. One value shared across every
19
+ * locale variant of the byline's translation group.
20
+ *
21
+ * The per-field `translatable` flag (column on `_emdash_byline_fields`)
22
+ * decides which value table a field writes to. The split is at the
23
+ * storage level rather than per-row so a single SELECT … IN per locale
24
+ * bucket suffices for batched hydration (see Phase 3 of the PR plan).
25
+ *
26
+ * `options('byline_fields_version', '0')` is the version counter the
27
+ * field-definitions cache reads (Phase 3). Every mutation on
28
+ * `_emdash_byline_fields` bumps this row; cached defs invalidate on a
29
+ * mismatch. Storing it in `options` (the same table `settings/index.ts`
30
+ * uses) means we get the request-cache + persisted-version pattern for
31
+ * free — no new infrastructure.
32
+ *
33
+ * Idempotency: every `CREATE TABLE` and `CREATE INDEX` uses
34
+ * `.ifNotExists()`, so a partial prior run (a crash mid-migration, retried
35
+ * after the runner's race-recovery path or after a manual fix) re-applies
36
+ * cleanly — including any indexes that landed in the failed pass after the
37
+ * table itself. A coarse table-level guard would skip the index step if the
38
+ * table existed but indexes didn't (the realistic crash window between
39
+ * `CREATE TABLE` and the next `CREATE INDEX`). The runner records applied
40
+ * migrations only after a successful pass, so a crashed-then-retried `up()`
41
+ * is the normal recovery path here.
42
+ *
43
+ * D1 has no transactions — the schema builder and `INSERT` for the
44
+ * version row execute one statement at a time. Order matters: parent
45
+ * tables first (`_emdash_byline_fields`), then the value tables that
46
+ * reference it. If `down()` runs after a partial `up()`, missing tables
47
+ * are tolerated (`DROP TABLE IF EXISTS`).
48
+ */
49
+
50
+ export async function up(db: Kysely<unknown>): Promise<void> {
51
+ await db.schema
52
+ .createTable("_emdash_byline_fields")
53
+ .ifNotExists()
54
+ .addColumn("id", "text", (col) => col.primaryKey())
55
+ .addColumn("slug", "text", (col) => col.notNull().unique())
56
+ .addColumn("label", "text", (col) => col.notNull())
57
+ .addColumn("type", "text", (col) => col.notNull())
58
+ .addColumn("required", "integer", (col) => col.notNull().defaultTo(0))
59
+ .addColumn("translatable", "integer", (col) => col.notNull().defaultTo(1))
60
+ .addColumn("validation", "text") // JSON: { options?: string[] }
61
+ .addColumn("sort_order", "integer", (col) => col.notNull().defaultTo(0))
62
+ .addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db)))
63
+ .addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db)))
64
+ .execute();
65
+
66
+ await db.schema
67
+ .createIndex("idx__emdash_byline_fields_sort_order")
68
+ .ifNotExists()
69
+ .on("_emdash_byline_fields")
70
+ .column("sort_order")
71
+ .execute();
72
+
73
+ await db.schema
74
+ .createTable("_emdash_byline_field_values")
75
+ .ifNotExists()
76
+ .addColumn("byline_id", "text", (col) =>
77
+ col.notNull().references("_emdash_bylines.id").onDelete("cascade"),
78
+ )
79
+ .addColumn("field_id", "text", (col) =>
80
+ col.notNull().references("_emdash_byline_fields.id").onDelete("cascade"),
81
+ )
82
+ .addColumn("value", "text") // JSON-encoded CustomFieldValue
83
+ .addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db)))
84
+ .addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db)))
85
+ .addPrimaryKeyConstraint("_emdash_byline_field_values_pk", ["byline_id", "field_id"])
86
+ .execute();
87
+
88
+ await db.schema
89
+ .createIndex("idx__emdash_byline_field_values_byline")
90
+ .ifNotExists()
91
+ .on("_emdash_byline_field_values")
92
+ .column("byline_id")
93
+ .execute();
94
+ await db.schema
95
+ .createIndex("idx__emdash_byline_field_values_field")
96
+ .ifNotExists()
97
+ .on("_emdash_byline_field_values")
98
+ .column("field_id")
99
+ .execute();
100
+
101
+ await db.schema
102
+ .createTable("_emdash_byline_field_group_values")
103
+ .ifNotExists()
104
+ .addColumn("translation_group", "text", (col) => col.notNull())
105
+ .addColumn("field_id", "text", (col) =>
106
+ col.notNull().references("_emdash_byline_fields.id").onDelete("cascade"),
107
+ )
108
+ .addColumn("value", "text") // JSON-encoded CustomFieldValue
109
+ .addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db)))
110
+ .addColumn("updated_at", "text", (col) => col.defaultTo(currentTimestamp(db)))
111
+ .addPrimaryKeyConstraint("_emdash_byline_field_group_values_pk", [
112
+ "translation_group",
113
+ "field_id",
114
+ ])
115
+ .execute();
116
+
117
+ await db.schema
118
+ .createIndex("idx__emdash_byline_field_group_values_group")
119
+ .ifNotExists()
120
+ .on("_emdash_byline_field_group_values")
121
+ .column("translation_group")
122
+ .execute();
123
+ await db.schema
124
+ .createIndex("idx__emdash_byline_field_group_values_field")
125
+ .ifNotExists()
126
+ .on("_emdash_byline_field_group_values")
127
+ .column("field_id")
128
+ .execute();
129
+
130
+ // Version-counter row read by the field-definitions cache (Phase 3).
131
+ // `options.value` stores JSON, so the initial counter is the JSON literal
132
+ // `0`. `INSERT … ON CONFLICT DO NOTHING` so a retry after a crash that
133
+ // landed the row but failed later in the migration does not double-insert.
134
+ await sql`
135
+ INSERT INTO options (name, value) VALUES ('byline_fields_version', '0')
136
+ ON CONFLICT (name) DO NOTHING
137
+ `.execute(db);
138
+ }
139
+
140
+ /**
141
+ * Reverses the schema additions and removes the version-counter row.
142
+ * Used by the test suite and by `rollbackMigration`; the runner itself
143
+ * never invokes `down()` automatically.
144
+ *
145
+ * The drops are unconditional `IF EXISTS` so a `down()` after a partial
146
+ * `up()` (only one or two tables landed) still settles the database back
147
+ * to its pre-042 state.
148
+ */
149
+ export async function down(db: Kysely<unknown>): Promise<void> {
150
+ // Child tables first to avoid FK reference issues on Postgres. SQLite +
151
+ // D1 accept either order with `IF EXISTS`, but explicit ordering keeps
152
+ // the dialects parity-safe.
153
+ await db.schema.dropTable("_emdash_byline_field_group_values").ifExists().execute();
154
+ await db.schema.dropTable("_emdash_byline_field_values").ifExists().execute();
155
+ await db.schema.dropTable("_emdash_byline_fields").ifExists().execute();
156
+ await sql`DELETE FROM options WHERE name = 'byline_fields_version'`.execute(db);
157
+ }