emdash 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (562) 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-CgamLmed.mjs} +93 -31
  14. package/dist/apply-CgamLmed.mjs.map +1 -0
  15. package/dist/astro/index.d.mts +10 -10
  16. package/dist/astro/index.mjs +19 -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-B2_XmnSU.d.mts → byline-fields-BNy7Ng1U.d.mts} +154 -26
  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 +29 -23
  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-BPZFAcgE.d.mts → index-CjKdMZ3U.d.mts} +39 -17
  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-CuvjwhrE.mjs} +12 -12
  376. package/dist/{query-7m6-l0f_.mjs.map → query-CuvjwhrE.mjs.map} +1 -1
  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-CgpzAU6F.mjs} +8 -8
  435. package/dist/{taxonomies-CcvrMLbR.mjs.map → taxonomies-CgpzAU6F.mjs.map} +1 -1
  436. package/dist/{taxonomies-4vx0nmMr.mjs → taxonomies-D72gTOg_.mjs} +4 -4
  437. package/dist/{taxonomies-4vx0nmMr.mjs.map → taxonomies-D72gTOg_.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-FGcv0ooe.mjs +7 -0
  481. package/dist/{version-BTc87L3L.mjs.map → version-FGcv0ooe.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/middleware/redirect.ts +5 -1
  500. package/src/astro/routes/api/admin/byline-fields/[slug]/usage.ts +36 -0
  501. package/src/astro/routes/api/admin/byline-fields/[slug].ts +92 -0
  502. package/src/astro/routes/api/admin/byline-fields/index.ts +66 -0
  503. package/src/astro/routes/api/admin/byline-fields/reorder.ts +39 -0
  504. package/src/astro/routes/api/admin/bylines/[id]/index.ts +23 -21
  505. package/src/astro/routes/api/admin/bylines/index.ts +1 -0
  506. package/src/astro/routes/api/auth/me.ts +21 -10
  507. package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +15 -3
  508. package/src/astro/routes/api/content/[collection]/[id].ts +3 -1
  509. package/src/astro/routes/api/media.ts +1 -0
  510. package/src/astro/types.ts +1 -0
  511. package/src/bylines/field-defs-cache.ts +138 -0
  512. package/src/bylines/index.ts +37 -4
  513. package/src/cli/commands/content.ts +4 -2
  514. package/src/client/index.ts +4 -1
  515. package/src/components/InlinePortableTextEditor.tsx +69 -0
  516. package/src/content/converters/portable-text-to-prosemirror.ts +7 -0
  517. package/src/content/converters/prosemirror-to-portable-text.ts +16 -0
  518. package/src/content/converters/types.ts +10 -0
  519. package/src/database/migrations/041_content_locale_list_index.ts +47 -0
  520. package/src/database/migrations/042_byline_fields.ts +157 -0
  521. package/src/database/migrations/runner.ts +4 -0
  522. package/src/database/repositories/byline.ts +758 -50
  523. package/src/database/repositories/content.ts +43 -3
  524. package/src/database/repositories/media.ts +14 -0
  525. package/src/database/repositories/types.ts +38 -0
  526. package/src/database/types.ts +44 -0
  527. package/src/emdash-runtime.ts +4 -1
  528. package/src/index.ts +1 -0
  529. package/src/loader.ts +98 -10
  530. package/src/mcp/server.ts +10 -1
  531. package/src/request-cache.ts +23 -0
  532. package/src/schema/byline-registry.ts +671 -0
  533. package/src/schema/registry.ts +14 -0
  534. package/src/schema/types.ts +133 -0
  535. package/src/seed/apply.ts +101 -14
  536. package/src/seed/types.ts +21 -0
  537. package/src/seed/validate.ts +39 -0
  538. package/dist/api-BNKqxyFX.mjs.map +0 -1
  539. package/dist/apply-BOPaD-s9.mjs.map +0 -1
  540. package/dist/byline-BDylH_m4.mjs +0 -404
  541. package/dist/byline-BDylH_m4.mjs.map +0 -1
  542. package/dist/bylines-B2_XmnSU.d.mts.map +0 -1
  543. package/dist/bylines-B7TFEvFf.mjs +0 -118
  544. package/dist/bylines-B7TFEvFf.mjs.map +0 -1
  545. package/dist/content-8voQNTXX.mjs.map +0 -1
  546. package/dist/error-ChfADBuu.mjs.map +0 -1
  547. package/dist/index-BPZFAcgE.d.mts.map +0 -1
  548. package/dist/loader-D-vIJjfY.mjs.map +0 -1
  549. package/dist/media-CKQd8AYU.mjs.map +0 -1
  550. package/dist/menus-C-nWT5Tu.mjs.map +0 -1
  551. package/dist/redirects-COMLwsV5.mjs.map +0 -1
  552. package/dist/runner-Drnvs96u.mjs.map +0 -1
  553. package/dist/setup-Cf_TyOv5.mjs +0 -137
  554. package/dist/setup-Cf_TyOv5.mjs.map +0 -1
  555. package/dist/types-B0bmgwMG.mjs.map +0 -1
  556. package/dist/types-DSZl1Dsv.mjs +0 -83
  557. package/dist/types-DaYDYW6g.d.mts.map +0 -1
  558. package/dist/validate-IGltez8n.mjs.map +0 -1
  559. package/dist/version-BTc87L3L.mjs +0 -7
  560. /package/dist/{api-tokens-iPIHAY8N.mjs → api-tokens-B6VgoE6M.mjs} +0 -0
  561. /package/dist/{ssrf-BIcd-aXW.mjs → ssrf-BvgVcfNQ.mjs} +0 -0
  562. /package/dist/{types-1NNkmTIn.mjs → types-Cj2S6FuC.mjs} +0 -0
@@ -1,11 +1,57 @@
1
1
  import type { Kysely } from "kysely";
2
2
 
3
- import { BylineRepository, type CreateBylineInput } from "../../database/repositories/byline.js";
4
- import type { BylineSummary } from "../../database/repositories/types.js";
3
+ import {
4
+ BylineRepository,
5
+ type CreateBylineInput,
6
+ type UpdateBylineInput,
7
+ } from "../../database/repositories/byline.js";
8
+ import { EmDashValidationError, type BylineSummary } from "../../database/repositories/types.js";
5
9
  import type { Database } from "../../database/types.js";
6
10
  import { getI18nConfig } from "../../i18n/config.js";
7
11
  import type { ApiResult } from "../types.js";
8
12
 
13
+ // `undefined → null` so a missing field in the create payload matches the
14
+ // repo's stored `null` (BylineRepository normalises with `?? null` on write).
15
+ const norm = (v: string | null | undefined): string | null => v ?? null;
16
+
17
+ /**
18
+ * Whether the existing byline row's fixed columns match a fresh-create
19
+ * payload after null/undefined normalisation. Used by the D1 create-retry
20
+ * recovery branch.
21
+ */
22
+ function bylineFixedFieldsMatch(
23
+ existing: BylineSummary,
24
+ input: CreateBylineInput,
25
+ effectiveLocale: string,
26
+ ): boolean {
27
+ return (
28
+ existing.displayName === input.displayName &&
29
+ norm(existing.bio) === norm(input.bio) &&
30
+ norm(existing.avatarMediaId) === norm(input.avatarMediaId) &&
31
+ norm(existing.websiteUrl) === norm(input.websiteUrl) &&
32
+ norm(existing.userId) === norm(input.userId) &&
33
+ existing.isGuest === (input.isGuest ?? false) &&
34
+ existing.locale === effectiveLocale
35
+ );
36
+ }
37
+
38
+ /**
39
+ * Whether every key in `existing` appears in `input` with the same value.
40
+ * Allows `input` to contain additional keys (the partial-write recovery
41
+ * case); rejects on a divergent value or a key the input omits.
42
+ */
43
+ function existingCustomFieldsAreSubsetOf(
44
+ existing: Record<string, unknown>,
45
+ input: Record<string, unknown> | undefined,
46
+ ): boolean {
47
+ if (!input) return Object.keys(existing).length === 0;
48
+ for (const [slug, value] of Object.entries(existing)) {
49
+ if (!Object.hasOwn(input, slug)) return false;
50
+ if (input[slug] !== value) return false;
51
+ }
52
+ return true;
53
+ }
54
+
9
55
  /**
10
56
  * Reject locales the site doesn't configure. Returns `null` when the locale
11
57
  * is fine (omitted, or matches `locales` in the i18n config, or i18n isn't
@@ -135,10 +181,30 @@ export async function handleBylineCreate(
135
181
  }
136
182
 
137
183
  // Duplicate guard: same (slug, locale) — matches the DB unique key
138
- // added in migration 040. Falls back to the configured defaultLocale
139
- // when the caller omits `locale`, mirroring the column DEFAULT.
184
+ // from migration 040.
140
185
  const existing = await repo.findBySlug(input.slug, { locale: effectiveLocale });
141
186
  if (existing) {
187
+ // D1 has no transactions, so a crash between the byline insert
188
+ // and the per-field writes leaves a partial row that's
189
+ // otherwise unrecoverable. Treat a same-identity retry that
190
+ // provides customFields as completing the abandoned create.
191
+ // Recovery requires fixed-column + translation-group +
192
+ // subset-customFields match; anything else collapses to a
193
+ // standard duplicate-slug conflict.
194
+ const expectedTranslationGroup = sourceGroup ?? existing.id;
195
+ const inputHasFields = !!input.customFields && Object.keys(input.customFields).length > 0;
196
+ if (
197
+ inputHasFields &&
198
+ bylineFixedFieldsMatch(existing, input, effectiveLocale) &&
199
+ existing.translationGroup === expectedTranslationGroup &&
200
+ existingCustomFieldsAreSubsetOf(existing.customFields ?? {}, input.customFields)
201
+ ) {
202
+ const recovered = await repo.update(existing.id, {
203
+ customFields: input.customFields,
204
+ });
205
+ if (recovered) return { success: true, data: recovered };
206
+ }
207
+
142
208
  return {
143
209
  success: false,
144
210
  error: {
@@ -152,10 +218,65 @@ export async function handleBylineCreate(
152
218
 
153
219
  const byline = await repo.create(input);
154
220
  return { success: true, data: byline };
155
- } catch {
221
+ } catch (error) {
222
+ // Mirror handleBylineUpdate: surface customFields validation
223
+ // errors as 400 rather than swallowing them as a generic 500.
224
+ if (error instanceof EmDashValidationError) {
225
+ return {
226
+ success: false,
227
+ error: { code: "VALIDATION_ERROR", message: error.message },
228
+ };
229
+ }
230
+ console.error("[BYLINE_CREATE_ERROR]", error);
156
231
  return {
157
232
  success: false,
158
233
  error: { code: "BYLINE_CREATE_ERROR", message: "Failed to create byline" },
159
234
  };
160
235
  }
161
236
  }
237
+
238
+ /**
239
+ * Update an existing byline. Forwards every field on `UpdateBylineInput`
240
+ * to `BylineRepository.update`, including the Phase 3 `customFields`
241
+ * map; per-field type validation lives in the repo, which throws
242
+ * `EmDashValidationError` on unknown slugs, type mismatches, or
243
+ * `select`-choice misses. This handler translates that into a clean
244
+ * `VALIDATION_ERROR` (400 via `mapErrorStatus`).
245
+ *
246
+ * Returns `NOT_FOUND` when the byline id doesn't resolve. Generic
247
+ * failures surface as `BYLINE_UPDATE_ERROR` (500) without leaking the
248
+ * underlying message.
249
+ */
250
+ export async function handleBylineUpdate(
251
+ db: Kysely<Database>,
252
+ id: string,
253
+ input: UpdateBylineInput,
254
+ ): Promise<ApiResult<BylineSummary>> {
255
+ try {
256
+ const repo = new BylineRepository(db);
257
+ const byline = await repo.update(id, input);
258
+ if (!byline) {
259
+ return {
260
+ success: false,
261
+ error: { code: "NOT_FOUND", message: "Byline not found" },
262
+ };
263
+ }
264
+ return { success: true, data: byline };
265
+ } catch (error) {
266
+ // Unknown-key + type-mismatch + select-choice writes throw
267
+ // EmDashValidationError (Phase 3, see BylineRepository.update).
268
+ // Map to a clean 400 — the error message names the offending
269
+ // slug/type, which is safe to surface to the admin client.
270
+ if (error instanceof EmDashValidationError) {
271
+ return {
272
+ success: false,
273
+ error: { code: "VALIDATION_ERROR", message: error.message },
274
+ };
275
+ }
276
+ console.error("[BYLINE_UPDATE_ERROR]", error);
277
+ return {
278
+ success: false,
279
+ error: { code: "BYLINE_UPDATE_ERROR", message: "Failed to update byline" },
280
+ };
281
+ }
282
+ }
@@ -295,6 +295,34 @@ export interface TrashedContentItem {
295
295
  deletedAt: string;
296
296
  }
297
297
 
298
+ /**
299
+ * Resolve the columns a content-list search should match against. Always
300
+ * includes `slug` (a standard column) and adds the `title`/`name` display
301
+ * fields when the collection actually defines them, mirroring the admin's
302
+ * item-title resolution (title -> name -> slug). Returning only existing
303
+ * columns avoids "no such column" errors on collections without them.
304
+ */
305
+ async function resolveSearchColumns(db: Kysely<Database>, collection: string): Promise<string[]> {
306
+ const columns = ["slug"];
307
+ const row = await db
308
+ .selectFrom("_emdash_collections")
309
+ .select("id")
310
+ .where("slug", "=", collection)
311
+ .executeTakeFirst();
312
+ if (!row) return columns;
313
+
314
+ const fields = await db
315
+ .selectFrom("_emdash_fields")
316
+ .select("slug")
317
+ .where("collection_id", "=", row.id)
318
+ .execute();
319
+ const fieldSlugs = new Set(fields.map((f) => f.slug));
320
+ for (const candidate of ["title", "name"]) {
321
+ if (fieldSlugs.has(candidate)) columns.push(candidate);
322
+ }
323
+ return columns;
324
+ }
325
+
298
326
  /**
299
327
  * Create content list handler
300
328
  */
@@ -308,14 +336,26 @@ export async function handleContentList(
308
336
  orderBy?: string;
309
337
  order?: "asc" | "desc";
310
338
  locale?: string;
339
+ q?: string;
311
340
  },
312
341
  ): Promise<ApiResult<ContentListResponse>> {
313
342
  try {
314
343
  const repo = new ContentRepository(db);
315
- const where: { status?: string; locale?: string } = {};
344
+ const where: {
345
+ status?: string;
346
+ locale?: string;
347
+ q?: string;
348
+ searchColumns?: string[];
349
+ } = {};
316
350
  if (params.status) where.status = params.status;
317
351
  if (params.locale) where.locale = params.locale;
318
352
 
353
+ const q = params.q?.trim();
354
+ if (q) {
355
+ where.q = q;
356
+ where.searchColumns = await resolveSearchColumns(db, collection);
357
+ }
358
+
319
359
  const result = await repo.findMany(collection, {
320
360
  cursor: params.cursor,
321
361
  limit: params.limit || 50,
@@ -655,6 +695,7 @@ export async function handleContentUpdate(
655
695
  status?: string;
656
696
  authorId?: string | null;
657
697
  bylines?: ContentBylineInput[];
698
+ locale?: string;
658
699
  _rev?: string;
659
700
  seo?: ContentSeoInput;
660
701
  publishedAt?: string | null;
@@ -682,7 +723,7 @@ export async function handleContentUpdate(
682
723
  const repo = new ContentRepository(db);
683
724
 
684
725
  // Resolve slug → ID if needed
685
- const resolvedId = (await resolveId(repo, collection, id)) ?? id;
726
+ const resolvedId = (await resolveId(repo, collection, id, body.locale)) ?? id;
686
727
 
687
728
  // Wrap content + SEO writes in a transaction for atomicity.
688
729
  // The _rev check is inside the transaction so the read-then-write
@@ -27,6 +27,7 @@ export async function handleMediaList(
27
27
  cursor?: string;
28
28
  limit?: number;
29
29
  mimeType?: string | readonly string[];
30
+ q?: string;
30
31
  },
31
32
  ): Promise<ApiResult<MediaListResponse>> {
32
33
  try {
@@ -35,6 +36,7 @@ export async function handleMediaList(
35
36
  cursor: params.cursor,
36
37
  limit: Math.min(params.limit || 50, 100),
37
38
  mimeType: params.mimeType,
39
+ q: params.q,
38
40
  });
39
41
 
40
42
  return {
@@ -257,6 +257,9 @@ const contentPaths = {
257
257
  collection: z.string().meta({ description: "Collection slug" }),
258
258
  id: z.string().meta({ description: "Content ID or slug" }),
259
259
  }),
260
+ query: z.object({
261
+ locale: z.string().optional().meta({ description: "Locale filter" }),
262
+ }),
260
263
  },
261
264
  requestBody: {
262
265
  content: { [JSON_CONTENT]: { schema: contentUpdateBody } },
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Zod schemas for the byline-fields admin API (Discussion #1174, Phase 4).
3
+ *
4
+ * Reserved-slug + identifier validation runs at the zod layer so the
5
+ * route returns a clean 400 (`VALIDATION_ERROR` from `parseBody`) rather
6
+ * than bubbling a registry-level `BylineSchemaError` ("RESERVED_SLUG" /
7
+ * "INVALID_SLUG"). The registry repeats the same checks for non-HTTP
8
+ * callers (seeds, scripts) — see `BylineSchemaRegistry.validateSlug`.
9
+ *
10
+ * Field types are constrained to the v1 subset declared in
11
+ * `BYLINE_FIELD_TYPES`. Adding a type to the union there will require a
12
+ * corresponding update to this enum.
13
+ */
14
+
15
+ import { z } from "zod";
16
+
17
+ import { BYLINE_FIELD_TYPES, RESERVED_BYLINE_FIELD_SLUGS } from "../../schema/types.js";
18
+
19
+ /**
20
+ * Slug pattern for byline field definitions — matches the identifier rule
21
+ * used by `validateIdentifier` (and `slugPattern` in `common.ts`).
22
+ * Lowercase letters, digits, and underscores; must start with a letter.
23
+ */
24
+ const bylineFieldSlugPattern = /^[a-z][a-z0-9_]*$/;
25
+
26
+ /** Hard cap on a slug — mirrors `BylineSchemaRegistry.MAX_SLUG_LENGTH`. */
27
+ const MAX_SLUG_LENGTH = 63;
28
+ /** Hard cap on a label — mirrors `BylineSchemaRegistry.MAX_LABEL_LENGTH`. */
29
+ const MAX_LABEL_LENGTH = 200;
30
+ /** Hard cap on a select field's `options` list. */
31
+ const MAX_SELECT_OPTIONS = 200;
32
+
33
+ const RESERVED_SET: ReadonlySet<string> = new Set(RESERVED_BYLINE_FIELD_SLUGS);
34
+
35
+ // Enumerate the v1 byline field types explicitly so zod gets the exact
36
+ // literal union for `z.infer<>`. Mirrors `BYLINE_FIELD_TYPES`; CI's
37
+ // type-checker catches drift via the satisfies/import below.
38
+ const bylineFieldTypeValues = z.enum(["string", "text", "url", "boolean", "select"]);
39
+ // Compile-time guard: a drift here trips the satisfies check.
40
+ type _BylineFieldTypeDriftCheck =
41
+ (typeof BYLINE_FIELD_TYPES)[number] extends z.infer<typeof bylineFieldTypeValues>
42
+ ? z.infer<typeof bylineFieldTypeValues> extends (typeof BYLINE_FIELD_TYPES)[number]
43
+ ? true
44
+ : never
45
+ : never;
46
+ const _bylineFieldTypeDriftCheck: _BylineFieldTypeDriftCheck = true;
47
+ void _bylineFieldTypeDriftCheck;
48
+
49
+ /**
50
+ * Validation payload for a byline custom field. v1 only exposes
51
+ * `options` (used by `select`-type fields). Empty/duplicate options are
52
+ * rejected at the registry layer; the zod layer only enforces shape and
53
+ * caps. Future field types may add keys here.
54
+ */
55
+ const bylineFieldValidationSchema = z
56
+ .object({
57
+ options: z
58
+ .array(z.string().min(1))
59
+ .min(1, "select options must contain at least one entry")
60
+ .max(MAX_SELECT_OPTIONS, `select options cannot exceed ${MAX_SELECT_OPTIONS} entries`)
61
+ .optional(),
62
+ })
63
+ .strict()
64
+ .nullable();
65
+
66
+ /**
67
+ * Slug validation chain shared by create + reorder bodies. Centralised so
68
+ * the reserved-slug message and pattern are identical everywhere.
69
+ */
70
+ const bylineFieldSlug = z
71
+ .string()
72
+ .min(1, "Byline field slug is required")
73
+ .max(MAX_SLUG_LENGTH, `Byline field slug must be ${MAX_SLUG_LENGTH} characters or less`)
74
+ .regex(
75
+ bylineFieldSlugPattern,
76
+ "Byline field slug must contain only lowercase letters, digits, and underscores, and start with a letter",
77
+ )
78
+ .refine((slug) => !RESERVED_SET.has(slug), {
79
+ // Surface the offending slug in the validation issue path-message
80
+ // for easier debugging from the admin UI's error toast.
81
+ message: "Byline field slug is reserved",
82
+ });
83
+
84
+ const bylineFieldLabel = z
85
+ .string()
86
+ .min(1, "Byline field label is required")
87
+ .max(MAX_LABEL_LENGTH, `Byline field label must be ${MAX_LABEL_LENGTH} characters or less`);
88
+
89
+ // ---------------------------------------------------------------------------
90
+ // Request bodies
91
+ // ---------------------------------------------------------------------------
92
+
93
+ export const bylineFieldCreateBody = z
94
+ .object({
95
+ slug: bylineFieldSlug,
96
+ label: bylineFieldLabel,
97
+ type: bylineFieldTypeValues,
98
+ required: z.boolean().optional(),
99
+ /**
100
+ * Whether values are stored per-locale (translatable, default) or
101
+ * shared across the translation group. See `BylineFieldDefinition`.
102
+ */
103
+ translatable: z.boolean().optional(),
104
+ validation: bylineFieldValidationSchema.optional(),
105
+ sortOrder: z.number().int().min(0).optional(),
106
+ })
107
+ .strict()
108
+ .meta({ id: "BylineFieldCreateBody" });
109
+
110
+ /**
111
+ * Update body. `slug` and `type` are intentionally absent — both are
112
+ * immutable post-create (changing them would invalidate stored values).
113
+ * `translatable` flips are gated at the registry layer when value rows
114
+ * exist (`TRANSLATABLE_LOCKED`).
115
+ */
116
+ export const bylineFieldUpdateBody = z
117
+ .object({
118
+ label: bylineFieldLabel.optional(),
119
+ required: z.boolean().optional(),
120
+ translatable: z.boolean().optional(),
121
+ validation: bylineFieldValidationSchema.optional(),
122
+ sortOrder: z.number().int().min(0).optional(),
123
+ })
124
+ .strict()
125
+ .meta({ id: "BylineFieldUpdateBody" });
126
+
127
+ export const bylineFieldReorderBody = z
128
+ .object({
129
+ /**
130
+ * Exact set of currently registered slugs in the desired order.
131
+ * The registry rejects any drift (`REORDER_MISMATCH`); the zod
132
+ * layer enforces slug shape only. An empty array is permitted —
133
+ * `reorderFields([])` is a valid no-op when zero fields are
134
+ * registered (registry contract). Rejecting empty here would
135
+ * produce a spurious 400 for an admin UI that submits a reorder
136
+ * after deleting the last field.
137
+ */
138
+ slugs: z.array(bylineFieldSlug),
139
+ })
140
+ .strict()
141
+ .meta({ id: "BylineFieldReorderBody" });
142
+
143
+ // ---------------------------------------------------------------------------
144
+ // Response shapes
145
+ // ---------------------------------------------------------------------------
146
+
147
+ export const bylineFieldDefinitionSchema = z
148
+ .object({
149
+ id: z.string(),
150
+ slug: z.string(),
151
+ label: z.string(),
152
+ type: bylineFieldTypeValues,
153
+ required: z.boolean(),
154
+ translatable: z.boolean(),
155
+ validation: z
156
+ .object({
157
+ options: z.array(z.string()).optional(),
158
+ })
159
+ .nullable(),
160
+ sortOrder: z.number().int(),
161
+ createdAt: z.string(),
162
+ updatedAt: z.string(),
163
+ })
164
+ .meta({ id: "BylineFieldDefinition" });
165
+
166
+ export const bylineFieldListResponseSchema = z
167
+ .object({
168
+ items: z.array(bylineFieldDefinitionSchema),
169
+ })
170
+ .meta({ id: "BylineFieldListResponse" });
171
+
172
+ /**
173
+ * Response shape for `GET /api/admin/byline-fields/[slug]/usage`.
174
+ *
175
+ * `translatableValueCount` counts rows in `_emdash_byline_field_values`.
176
+ * `groupValueCount` counts rows in `_emdash_byline_field_group_values`.
177
+ * `totalAffectedRows` is the sum — what the destructive-delete confirm
178
+ * dialog surfaces. Both individual counts are exposed for diagnostic
179
+ * value (e.g. inconsistency with the field's current `translatable`
180
+ * flag would show non-zero on the "wrong" side).
181
+ */
182
+ export const bylineFieldUsageResponseSchema = z
183
+ .object({
184
+ translatableValueCount: z.number().int().nonnegative(),
185
+ groupValueCount: z.number().int().nonnegative(),
186
+ totalAffectedRows: z.number().int().nonnegative(),
187
+ })
188
+ .meta({ id: "BylineFieldUsageResponse" });
@@ -12,6 +12,13 @@ export const bylineSummarySchema = z
12
12
  displayName: z.string(),
13
13
  bio: z.string().nullable(),
14
14
  avatarMediaId: z.string().nullable(),
15
+ /**
16
+ * Avatar media storage key + alt, folded in by the media join during
17
+ * content byline hydration. Null on the plain byline finders, which
18
+ * don't join media.
19
+ */
20
+ avatarStorageKey: z.string().nullish(),
21
+ avatarAlt: z.string().nullish(),
15
22
  websiteUrl: z.string().nullable(),
16
23
  userId: z.string().nullable(),
17
24
  isGuest: z.boolean(),
@@ -25,6 +32,16 @@ export const bylineSummarySchema = z
25
32
  * source. Nullable in storage for backwards compatibility.
26
33
  */
27
34
  translationGroup: z.string().nullable(),
35
+ /**
36
+ * Byline custom-field values (Discussion #1174). Keys are slugs
37
+ * registered via the byline-fields admin API; values follow
38
+ * `CustomFieldValue` (`string | boolean | null`). Always present
39
+ * on hydrated responses — empty `{}` when no fields are
40
+ * registered (Phase 3 AC #6). Marked optional in the schema for
41
+ * historic-payload compatibility with pre-Phase-3 clients that
42
+ * may not send the key on writes; hydration always populates it.
43
+ */
44
+ customFields: z.record(z.string(), z.union([z.string(), z.boolean(), z.null()])).optional(),
28
45
  })
29
46
  .meta({ id: "BylineSummary" });
30
47
 
@@ -83,6 +100,19 @@ export const bylineCreateBody = z
83
100
  * rather than minting a fresh one. Requires `locale`.
84
101
  */
85
102
  translationOf: z.string().min(1).optional(),
103
+ /**
104
+ * Byline custom-field values (Discussion #1174, Phase 6 — create-flow
105
+ * parity with update). Keys are field slugs; values are unknown at
106
+ * the API layer because the per-field type contract lives in the
107
+ * registry and would require an extra query to enforce here. The
108
+ * repository's `coerceFieldValue` validates against the field's
109
+ * type and throws `EmDashValidationError` on mismatch — the route
110
+ * maps that to a 400 `VALIDATION_ERROR`. Reserved-slug write
111
+ * attempts fall out as `EmDashValidationError("Unknown byline
112
+ * custom field …")` because no registered field claims a reserved
113
+ * slug.
114
+ */
115
+ customFields: z.record(z.string(), z.unknown()).optional(),
86
116
  })
87
117
  .meta({ id: "BylineCreateBody" });
88
118
 
@@ -120,6 +150,18 @@ export const bylineUpdateBody = z
120
150
  websiteUrl: httpUrl.nullish(),
121
151
  userId: z.string().nullish(),
122
152
  isGuest: z.boolean().optional(),
153
+ /**
154
+ * Byline custom-field values (Discussion #1174, Phase 3+4). Keys
155
+ * are field slugs; values are unknown at the API layer because
156
+ * the per-field type contract lives in the registry and would
157
+ * require an extra query to enforce here. The repository's
158
+ * `coerceFieldValue` validates against the field's type and
159
+ * throws `EmDashValidationError` on mismatch — the route maps
160
+ * that to a 400 `VALIDATION_ERROR`. Reserved-slug write attempts
161
+ * fall out as `EmDashValidationError("Unknown byline custom
162
+ * field …")` because no registered field claims a reserved slug.
163
+ */
164
+ customFields: z.record(z.string(), z.unknown()).optional(),
123
165
  })
124
166
  .meta({ id: "BylineUpdateBody" });
125
167
 
@@ -24,6 +24,8 @@ export const contentListQuery = cursorPaginationQuery
24
24
  orderBy: z.string().optional(),
25
25
  order: z.enum(["asc", "desc"]).optional(),
26
26
  locale: localeCode.optional(),
27
+ /** Case-insensitive substring search across the collection's title/name/slug. */
28
+ q: z.string().trim().min(1).max(200).optional(),
27
29
  })
28
30
  .meta({ id: "ContentListQuery" });
29
31
 
@@ -15,3 +15,4 @@ export * from "./users.js";
15
15
  export * from "./widgets.js";
16
16
  export * from "./redirects.js";
17
17
  export * from "./bylines.js";
18
+ export * from "./byline-fields.js";
@@ -21,6 +21,8 @@ const mimeTypeFilter = z
21
21
  export const mediaListQuery = cursorPaginationQuery
22
22
  .extend({
23
23
  mimeType: mimeTypeFilter,
24
+ /** Case-insensitive filename substring search (also matches extensions). */
25
+ q: z.string().trim().min(1).max(200).optional(),
24
26
  })
25
27
  .meta({ id: "MediaListQuery" });
26
28
 
@@ -457,6 +457,33 @@ export function injectCoreRoutes(injectRoute: InjectRoute): void {
457
457
  entrypoint: resolveRoute("api/admin/bylines/[id]/translations.ts"),
458
458
  });
459
459
 
460
+ // Byline custom-field schema routes (Discussion #1174, Phase 4).
461
+ // Order matters: the static `reorder` route must precede the dynamic
462
+ // `[slug]` route so Astro's resolver dispatches POST /byline-fields/reorder
463
+ // to the reorder handler instead of treating "reorder" as a slug. The
464
+ // `reorder` slug is also reserved at the data layer
465
+ // (RESERVED_BYLINE_FIELD_SLUGS) so the registry rejects field creation
466
+ // with that name — defence in depth.
467
+ injectRoute({
468
+ pattern: "/_emdash/api/admin/byline-fields",
469
+ entrypoint: resolveRoute("api/admin/byline-fields/index.ts"),
470
+ });
471
+
472
+ injectRoute({
473
+ pattern: "/_emdash/api/admin/byline-fields/reorder",
474
+ entrypoint: resolveRoute("api/admin/byline-fields/reorder.ts"),
475
+ });
476
+
477
+ injectRoute({
478
+ pattern: "/_emdash/api/admin/byline-fields/[slug]",
479
+ entrypoint: resolveRoute("api/admin/byline-fields/[slug].ts"),
480
+ });
481
+
482
+ injectRoute({
483
+ pattern: "/_emdash/api/admin/byline-fields/[slug]/usage",
484
+ entrypoint: resolveRoute("api/admin/byline-fields/[slug]/usage.ts"),
485
+ });
486
+
460
487
  injectRoute({
461
488
  pattern: "/_emdash/api/admin/users/[id]",
462
489
  entrypoint: resolveRoute("api/admin/users/[id]/index.ts"),
@@ -74,7 +74,11 @@ export const onRequest = defineMiddleware(async (context, next) => {
74
74
  }
75
75
 
76
76
  // 1. Exact match (O(1) Map lookup)
77
- const exact = cached.exact.get(pathname);
77
+ let exact = cached.exact.get(pathname);
78
+ if (!exact && pathname.length > 1) {
79
+ const alt = pathname.endsWith("/") ? pathname.slice(0, -1) : `${pathname}/`;
80
+ exact = cached.exact.get(alt);
81
+ }
78
82
  if (exact) {
79
83
  const dest = exact.destination;
80
84
  if (dest.startsWith("//") || dest.startsWith("/\\")) return next();
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Byline field usage counts.
3
+ *
4
+ * GET /_emdash/api/admin/byline-fields/{slug}/usage
5
+ *
6
+ * Returns `{ translatableValueCount, groupValueCount, totalAffectedRows }`.
7
+ * Backs the destructive-delete confirm dialog in the admin UI (Phase 5).
8
+ * Thin wrapper around `handleBylineFieldUsage`.
9
+ *
10
+ * Phase 4 of Discussion #1174.
11
+ */
12
+
13
+ import type { APIRoute } from "astro";
14
+
15
+ import { requirePerm } from "#api/authorize.js";
16
+ import { apiError, requireDb, unwrapResult } from "#api/error.js";
17
+ import { handleBylineFieldUsage } from "#api/handlers/byline-fields.js";
18
+
19
+ export const prerender = false;
20
+
21
+ // GET requires `schema:read` (Editor+); see byline-fields/index.ts GET
22
+ // for rationale on the read/manage split.
23
+ export const GET: APIRoute = async ({ params, locals }) => {
24
+ const { emdash, user } = locals;
25
+ const denied = requirePerm(user, "schema:read");
26
+ if (denied) return denied;
27
+
28
+ const dbErr = requireDb(emdash?.db);
29
+ if (dbErr) return dbErr;
30
+
31
+ const slug = params.slug;
32
+ if (!slug) return apiError("MISSING_PARAM", "Field slug is required", 400);
33
+
34
+ const result = await handleBylineFieldUsage(emdash.db, slug);
35
+ return unwrapResult(result);
36
+ };