emdash 0.17.0 → 0.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/dist/api/route-utils.mjs +11 -11
  2. package/dist/api/schemas/index.d.mts +1 -1
  3. package/dist/{api-Dmz40c2V.mjs → api-B7GATEYo.mjs} +12 -12
  4. package/dist/{api-Dmz40c2V.mjs.map → api-B7GATEYo.mjs.map} +1 -1
  5. package/dist/{apply-CgamLmed.mjs → apply-BrVqULFe.mjs} +16 -16
  6. package/dist/{apply-CgamLmed.mjs.map → apply-BrVqULFe.mjs.map} +1 -1
  7. package/dist/astro/index.d.mts +2 -2
  8. package/dist/astro/index.mjs +10 -1
  9. package/dist/astro/index.mjs.map +1 -1
  10. package/dist/astro/middleware/auth.d.mts +2 -2
  11. package/dist/astro/middleware/auth.mjs +2 -2
  12. package/dist/astro/middleware/redirect.mjs +5 -5
  13. package/dist/astro/middleware.d.mts.map +1 -1
  14. package/dist/astro/middleware.mjs +65 -49
  15. package/dist/astro/middleware.mjs.map +1 -1
  16. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +3 -3
  17. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +3 -3
  18. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +2 -2
  19. package/dist/astro/routes/api/admin/api-tokens/index.mjs +3 -3
  20. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +3 -3
  21. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +4 -4
  22. package/dist/astro/routes/api/admin/byline-fields/index.mjs +4 -4
  23. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +4 -4
  24. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +9 -9
  25. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +9 -9
  26. package/dist/astro/routes/api/admin/bylines/index.mjs +9 -9
  27. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +7 -7
  28. package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
  29. package/dist/astro/routes/api/admin/comments/bulk.mjs +6 -6
  30. package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
  31. package/dist/astro/routes/api/admin/comments/index.mjs +6 -6
  32. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
  33. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  34. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +3 -3
  35. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +3 -3
  36. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +26 -26
  37. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +26 -26
  38. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +26 -26
  39. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +26 -26
  40. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +26 -26
  41. package/dist/astro/routes/api/admin/plugins/index.mjs +26 -26
  42. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
  43. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +26 -26
  44. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +26 -26
  45. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +26 -26
  46. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +26 -26
  47. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +27 -27
  48. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +26 -26
  49. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +27 -27
  50. package/dist/astro/routes/api/admin/plugins/updates.mjs +26 -26
  51. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +26 -26
  52. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
  53. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +26 -26
  54. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
  55. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
  56. package/dist/astro/routes/api/admin/users/_id_/index.mjs +3 -3
  57. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +2 -2
  58. package/dist/astro/routes/api/admin/users/index.mjs +3 -3
  59. package/dist/astro/routes/api/auth/dev-bypass.mjs +4 -4
  60. package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
  61. package/dist/astro/routes/api/auth/invite/complete.mjs +3 -3
  62. package/dist/astro/routes/api/auth/invite/index.mjs +3 -3
  63. package/dist/astro/routes/api/auth/invite/register-options.mjs +3 -3
  64. package/dist/astro/routes/api/auth/logout.mjs +2 -2
  65. package/dist/astro/routes/api/auth/magic-link/send.mjs +4 -4
  66. package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
  67. package/dist/astro/routes/api/auth/me.mjs +4 -4
  68. package/dist/astro/routes/api/auth/passkey/_id_.mjs +3 -3
  69. package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
  70. package/dist/astro/routes/api/auth/passkey/options.mjs +4 -4
  71. package/dist/astro/routes/api/auth/passkey/register/options.mjs +3 -3
  72. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +3 -3
  73. package/dist/astro/routes/api/auth/passkey/verify.mjs +3 -3
  74. package/dist/astro/routes/api/auth/signup/complete.mjs +3 -3
  75. package/dist/astro/routes/api/auth/signup/request.mjs +4 -4
  76. package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
  77. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +6 -6
  78. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
  79. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
  80. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
  81. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
  82. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +4 -4
  83. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +4 -4
  84. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
  85. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
  86. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +4 -4
  87. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +9 -9
  88. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
  89. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
  90. package/dist/astro/routes/api/content/_collection_/_id_.mjs +4 -4
  91. package/dist/astro/routes/api/content/_collection_/index.mjs +4 -4
  92. package/dist/astro/routes/api/content/_collection_/trash.mjs +4 -4
  93. package/dist/astro/routes/api/dashboard.mjs +7 -7
  94. package/dist/astro/routes/api/dev/emails.mjs +2 -2
  95. package/dist/astro/routes/api/import/probe.mjs +4 -4
  96. package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
  97. package/dist/astro/routes/api/import/wordpress/execute.d.mts +2 -2
  98. package/dist/astro/routes/api/import/wordpress/execute.mjs +8 -8
  99. package/dist/astro/routes/api/import/wordpress/media.mjs +4 -4
  100. package/dist/astro/routes/api/import/wordpress/prepare.mjs +6 -6
  101. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +5 -5
  102. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +4 -4
  103. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +6 -6
  104. package/dist/astro/routes/api/manifest.mjs +3 -3
  105. package/dist/astro/routes/api/mcp.mjs +26 -26
  106. package/dist/astro/routes/api/media/_id_/confirm.mjs +4 -4
  107. package/dist/astro/routes/api/media/_id_.mjs +4 -4
  108. package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
  109. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
  110. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
  111. package/dist/astro/routes/api/media/providers/index.mjs +3 -3
  112. package/dist/astro/routes/api/media/upload-url.mjs +4 -4
  113. package/dist/astro/routes/api/media.mjs +5 -5
  114. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +5 -5
  115. package/dist/astro/routes/api/menus/_name_/items.mjs +5 -5
  116. package/dist/astro/routes/api/menus/_name_/reorder.mjs +5 -5
  117. package/dist/astro/routes/api/menus/_name_/translations.mjs +5 -5
  118. package/dist/astro/routes/api/menus/_name_.mjs +5 -5
  119. package/dist/astro/routes/api/menus/index.mjs +5 -5
  120. package/dist/astro/routes/api/oauth/device/authorize.mjs +3 -3
  121. package/dist/astro/routes/api/oauth/device/code.mjs +4 -4
  122. package/dist/astro/routes/api/oauth/device/token.mjs +4 -4
  123. package/dist/astro/routes/api/oauth/register.mjs +2 -2
  124. package/dist/astro/routes/api/oauth/token/refresh.mjs +3 -3
  125. package/dist/astro/routes/api/oauth/token/revoke.mjs +3 -3
  126. package/dist/astro/routes/api/oauth/token.mjs +2 -2
  127. package/dist/astro/routes/api/openapi.json.mjs +2 -2
  128. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +3 -3
  129. package/dist/astro/routes/api/redirects/404s/index.mjs +7 -7
  130. package/dist/astro/routes/api/redirects/404s/summary.mjs +7 -7
  131. package/dist/astro/routes/api/redirects/_id_.mjs +8 -8
  132. package/dist/astro/routes/api/redirects/index.mjs +8 -8
  133. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
  134. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
  135. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +26 -26
  136. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +26 -26
  137. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +26 -26
  138. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +26 -26
  139. package/dist/astro/routes/api/schema/collections/index.mjs +26 -26
  140. package/dist/astro/routes/api/schema/index.mjs +7 -7
  141. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +26 -26
  142. package/dist/astro/routes/api/schema/orphans/index.mjs +26 -26
  143. package/dist/astro/routes/api/search/enable.mjs +8 -8
  144. package/dist/astro/routes/api/search/index.mjs +7 -7
  145. package/dist/astro/routes/api/search/rebuild.mjs +8 -8
  146. package/dist/astro/routes/api/search/stats.mjs +7 -7
  147. package/dist/astro/routes/api/search/suggest.mjs +7 -7
  148. package/dist/astro/routes/api/sections/_slug_.mjs +7 -7
  149. package/dist/astro/routes/api/sections/index.mjs +7 -7
  150. package/dist/astro/routes/api/settings/email.mjs +4 -4
  151. package/dist/astro/routes/api/settings.mjs +9 -9
  152. package/dist/astro/routes/api/setup/admin-verify.mjs +3 -3
  153. package/dist/astro/routes/api/setup/admin.mjs +3 -3
  154. package/dist/astro/routes/api/setup/dev-bypass.mjs +16 -16
  155. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  156. package/dist/astro/routes/api/setup/index.mjs +17 -17
  157. package/dist/astro/routes/api/setup/status.mjs +3 -3
  158. package/dist/astro/routes/api/snapshot.mjs +3 -3
  159. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +9 -9
  160. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +9 -9
  161. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +9 -9
  162. package/dist/astro/routes/api/taxonomies/index.mjs +9 -9
  163. package/dist/astro/routes/api/themes/preview.mjs +3 -3
  164. package/dist/astro/routes/api/typegen.mjs +5 -5
  165. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +4 -4
  166. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +7 -7
  167. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +7 -7
  168. package/dist/astro/routes/api/widget-areas/_name_.mjs +6 -6
  169. package/dist/astro/routes/api/widget-areas/index.mjs +7 -7
  170. package/dist/astro/routes/api/widget-components.mjs +2 -2
  171. package/dist/astro/routes/robots.txt.mjs +5 -5
  172. package/dist/astro/routes/sitemap-_collection_.xml.mjs +5 -5
  173. package/dist/astro/routes/sitemap.xml.mjs +5 -5
  174. package/dist/astro/types.d.mts +2 -2
  175. package/dist/{authorize-_wWM_44T.mjs → authorize-CLTmOUyx.mjs} +2 -2
  176. package/dist/{authorize-_wWM_44T.mjs.map → authorize-CLTmOUyx.mjs.map} +1 -1
  177. package/dist/{byline-BrIVWLm-.mjs → byline-CAhk4FrG.mjs} +4 -4
  178. package/dist/{byline-BrIVWLm-.mjs.map → byline-CAhk4FrG.mjs.map} +1 -1
  179. package/dist/{byline-fields-BNy7Ng1U.d.mts → byline-fields-CR5hGLMw.d.mts} +28 -28
  180. package/dist/{byline-fields-BNy7Ng1U.d.mts.map → byline-fields-CR5hGLMw.d.mts.map} +1 -1
  181. package/dist/{bylines-sqExMElV.mjs → bylines-CbrD7STW.mjs} +3 -3
  182. package/dist/{bylines-sqExMElV.mjs.map → bylines-CbrD7STW.mjs.map} +1 -1
  183. package/dist/{bylines-C_POWmGT.mjs → bylines-DCczH3AV.mjs} +4 -4
  184. package/dist/{bylines-C_POWmGT.mjs.map → bylines-DCczH3AV.mjs.map} +1 -1
  185. package/dist/{cache-wsDkA8ru.mjs → cache-DIHHyPkt.mjs} +2 -2
  186. package/dist/{cache-wsDkA8ru.mjs.map → cache-DIHHyPkt.mjs.map} +1 -1
  187. package/dist/{chunks-BAYkM-CF.mjs → chunks-DnnHlRG3.mjs} +2 -2
  188. package/dist/{chunks-BAYkM-CF.mjs.map → chunks-DnnHlRG3.mjs.map} +1 -1
  189. package/dist/cli/index.mjs +125 -23
  190. package/dist/cli/index.mjs.map +1 -1
  191. package/dist/{comment-Cd29aktf.mjs → comment-DkAfGX9E.mjs} +2 -2
  192. package/dist/{comment-Cd29aktf.mjs.map → comment-DkAfGX9E.mjs.map} +1 -1
  193. package/dist/{comments-B7ufhkxN.mjs → comments-DLFnXs7J.mjs} +3 -3
  194. package/dist/{comments-B7ufhkxN.mjs.map → comments-DLFnXs7J.mjs.map} +1 -1
  195. package/dist/{content-BbqKo3Kc.mjs → content-C7aJ7keg.mjs} +3 -3
  196. package/dist/{content-BbqKo3Kc.mjs.map → content-C7aJ7keg.mjs.map} +1 -1
  197. package/dist/{context-BsF1rhoI.mjs → context-Ca0HkaIh.mjs} +8 -8
  198. package/dist/{context-BsF1rhoI.mjs.map → context-Ca0HkaIh.mjs.map} +1 -1
  199. package/dist/{dashboard-BwIX9r-X.mjs → dashboard-BrfLIsX1.mjs} +4 -4
  200. package/dist/{dashboard-BwIX9r-X.mjs.map → dashboard-BrfLIsX1.mjs.map} +1 -1
  201. package/dist/db/index.mjs +2 -2
  202. package/dist/{dialect-helpers-BKCvISIQ.mjs → dialect-helpers-DRI5pyY3.mjs} +3 -3
  203. package/dist/dialect-helpers-DRI5pyY3.mjs.map +1 -0
  204. package/dist/{error-npZWBSb7.mjs → error-Bk9s3Ism.mjs} +2 -2
  205. package/dist/{error-npZWBSb7.mjs.map → error-Bk9s3Ism.mjs.map} +1 -1
  206. package/dist/{fts-manager-DmUAk-kQ.mjs → fts-manager-XpDfbIKo.mjs} +3 -3
  207. package/dist/{fts-manager-DmUAk-kQ.mjs.map → fts-manager-XpDfbIKo.mjs.map} +1 -1
  208. package/dist/{index-CjKdMZ3U.d.mts → index-C8ciqSMJ.d.mts} +4 -4
  209. package/dist/{index-CjKdMZ3U.d.mts.map → index-C8ciqSMJ.d.mts.map} +1 -1
  210. package/dist/index.d.mts +2 -2
  211. package/dist/index.mjs +35 -35
  212. package/dist/{load-DsoLq7ex.mjs → load-CF5oETkh.mjs} +2 -2
  213. package/dist/{load-DsoLq7ex.mjs.map → load-CF5oETkh.mjs.map} +1 -1
  214. package/dist/{loader-CJ6lWO0d.mjs → loader-BxyvbrZP.mjs} +4 -4
  215. package/dist/{loader-CJ6lWO0d.mjs.map → loader-BxyvbrZP.mjs.map} +1 -1
  216. package/dist/media/local-runtime.d.mts +2 -2
  217. package/dist/media/local-runtime.mjs +5 -5
  218. package/dist/{media-jk_HzzOl.mjs → media-Cyz5BhSN.mjs} +2 -2
  219. package/dist/{media-jk_HzzOl.mjs.map → media-Cyz5BhSN.mjs.map} +1 -1
  220. package/dist/{menus-CyMO6GBx.mjs → menus-CIdZ_Q6U.mjs} +4 -4
  221. package/dist/{menus-CyMO6GBx.mjs.map → menus-CIdZ_Q6U.mjs.map} +1 -1
  222. package/dist/{menus-B-5-3aon.mjs → menus-PFp8FDuO.mjs} +2 -2
  223. package/dist/{menus-B-5-3aon.mjs.map → menus-PFp8FDuO.mjs.map} +1 -1
  224. package/dist/{parse-4zO5Y2DL.mjs → parse-B-K21lvm.mjs} +2 -2
  225. package/dist/{parse-4zO5Y2DL.mjs.map → parse-B-K21lvm.mjs.map} +1 -1
  226. package/dist/plugin-utils.d.mts +2 -2
  227. package/dist/plugins/adapt-sandbox-entry.d.mts +2 -2
  228. package/dist/{query-CuvjwhrE.mjs → query-Cc649nDl.mjs} +17 -16
  229. package/dist/query-Cc649nDl.mjs.map +1 -0
  230. package/dist/{rate-limit-D6VQqBk_.mjs → rate-limit-BI1OdpQH.mjs} +2 -2
  231. package/dist/{rate-limit-D6VQqBk_.mjs.map → rate-limit-BI1OdpQH.mjs.map} +1 -1
  232. package/dist/{redirect-BZUJltlj.mjs → redirect-C-FeA4j9.mjs} +3 -3
  233. package/dist/{redirect-BZUJltlj.mjs.map → redirect-C-FeA4j9.mjs.map} +1 -1
  234. package/dist/{redirects-DnYuqsEf.mjs → redirects-C1UgU9E0.mjs} +3 -3
  235. package/dist/{redirects-DnYuqsEf.mjs.map → redirects-C1UgU9E0.mjs.map} +1 -1
  236. package/dist/{registry-Dn6gsx3L.mjs → registry-C-T_PWgp.mjs} +5 -5
  237. package/dist/{registry-Dn6gsx3L.mjs.map → registry-C-T_PWgp.mjs.map} +1 -1
  238. package/dist/{runner-eAgyIkeg.mjs → runner-BiuUfx-V.mjs} +4 -4
  239. package/dist/runner-BiuUfx-V.mjs.map +1 -0
  240. package/dist/runtime.d.mts +2 -2
  241. package/dist/runtime.mjs +3 -3
  242. package/dist/{schema--mYZX4D7.mjs → schema-BpCJh2lU.mjs} +4 -4
  243. package/dist/{schema--mYZX4D7.mjs.map → schema-BpCJh2lU.mjs.map} +1 -1
  244. package/dist/{search-C6U_NvZI.mjs → search-BrF7k0Ho.mjs} +4 -4
  245. package/dist/{search-C6U_NvZI.mjs.map → search-BrF7k0Ho.mjs.map} +1 -1
  246. package/dist/{sections-Ba-rJLKb.mjs → sections-8DEa-dWt.mjs} +3 -3
  247. package/dist/{sections-Ba-rJLKb.mjs.map → sections-8DEa-dWt.mjs.map} +1 -1
  248. package/dist/seed/index.mjs +14 -14
  249. package/dist/seo/index.mjs +1 -0
  250. package/dist/seo/index.mjs.map +1 -1
  251. package/dist/{seo-BTzb5ksq.mjs → seo-CKr7pLfA.mjs} +2 -2
  252. package/dist/{seo-BTzb5ksq.mjs.map → seo-CKr7pLfA.mjs.map} +1 -1
  253. package/dist/{service-Cn-kIfZn.mjs → service-9P2cdyR_.mjs} +2 -2
  254. package/dist/{service-Cn-kIfZn.mjs.map → service-9P2cdyR_.mjs.map} +1 -1
  255. package/dist/{settings-C65OSm41.mjs → settings-DYVzINdn.mjs} +3 -3
  256. package/dist/{settings-C65OSm41.mjs.map → settings-DYVzINdn.mjs.map} +1 -1
  257. package/dist/{settings-ChlQbwU0.mjs → settings-Jro4YcUb.mjs} +3 -3
  258. package/dist/{settings-ChlQbwU0.mjs.map → settings-Jro4YcUb.mjs.map} +1 -1
  259. package/dist/{taxonomies-D72gTOg_.mjs → taxonomies-C0bVme_m.mjs} +4 -4
  260. package/dist/{taxonomies-D72gTOg_.mjs.map → taxonomies-C0bVme_m.mjs.map} +1 -1
  261. package/dist/{taxonomies-CgpzAU6F.mjs → taxonomies-CGD6y79Q.mjs} +5 -5
  262. package/dist/{taxonomies-CgpzAU6F.mjs.map → taxonomies-CGD6y79Q.mjs.map} +1 -1
  263. package/dist/{taxonomy-BBK-UAEo.mjs → taxonomy-Db5xwphL.mjs} +3 -3
  264. package/dist/{taxonomy-BBK-UAEo.mjs.map → taxonomy-Db5xwphL.mjs.map} +1 -1
  265. package/dist/{types-SF1DwGf2.mjs → types-CfyYQ7eY.mjs} +2 -2
  266. package/dist/{types-SF1DwGf2.mjs.map → types-CfyYQ7eY.mjs.map} +1 -1
  267. package/dist/{user-X4rtyO4Y.mjs → user-tLdHUEXV.mjs} +2 -2
  268. package/dist/{user-X4rtyO4Y.mjs.map → user-tLdHUEXV.mjs.map} +1 -1
  269. package/dist/{validate-DactmcJG.mjs → validate-DWmnRg6E.mjs} +2 -2
  270. package/dist/{validate-DactmcJG.mjs.map → validate-DWmnRg6E.mjs.map} +1 -1
  271. package/dist/{validation-BYA4i85b.mjs → validation-BQ_TP-On.mjs} +6 -6
  272. package/dist/{validation-BYA4i85b.mjs.map → validation-BQ_TP-On.mjs.map} +1 -1
  273. package/dist/version-CgcnMvqS.mjs +7 -0
  274. package/dist/{version-FGcv0ooe.mjs.map → version-CgcnMvqS.mjs.map} +1 -1
  275. package/dist/{widgets-DG-1jxnz.mjs → widgets-DzlINGI6.mjs} +2 -2
  276. package/dist/{widgets-DG-1jxnz.mjs.map → widgets-DzlINGI6.mjs.map} +1 -1
  277. package/dist/{zod-generator-BNAObjSt.mjs → zod-generator-MMm56Prt.mjs} +2 -2
  278. package/dist/{zod-generator-BNAObjSt.mjs.map → zod-generator-MMm56Prt.mjs.map} +1 -1
  279. package/package.json +6 -6
  280. package/src/astro/integration/vite-config.ts +16 -0
  281. package/src/astro/middleware.ts +34 -8
  282. package/src/cli/commands/export-seed.ts +174 -12
  283. package/src/database/dialect-helpers.ts +8 -2
  284. package/src/database/migrations/019_i18n.ts +2 -2
  285. package/src/query.ts +7 -7
  286. package/src/seo/index.ts +10 -1
  287. package/dist/dialect-helpers-BKCvISIQ.mjs.map +0 -1
  288. package/dist/query-CuvjwhrE.mjs.map +0 -1
  289. package/dist/runner-eAgyIkeg.mjs.map +0 -1
  290. package/dist/version-FGcv0ooe.mjs +0 -7
@@ -1 +1 @@
1
- {"version":3,"file":"byline-BrIVWLm-.mjs","names":[],"sources":["../src/bylines/field-defs-cache.ts","../src/database/repositories/byline.ts"],"sourcesContent":["/**\n * Byline field-definitions cache\n *\n * Discussion #1174 / Phase 3. Two-tier cache for the byline custom-field\n * registry, mirroring the `settings/index.ts` pattern.\n *\n * **Tier 1 — per-isolate (globalThis).** Field definitions change rarely\n * but are read on every byline hydration (admin pages, content rendering,\n * API responses). Caching at the isolate level drops the SELECT-from-\n * `_emdash_byline_fields` from once-per-hydration to once-per-isolate-\n * after-bump. The cache holds a Promise (not the resolved value) so\n * concurrent cold-isolate readers share the in-flight query.\n *\n * Stored on globalThis under `Symbol.for(\"emdash:byline-field-defs\")` so\n * Vite SSR chunk duplication can't produce two independent caches (same\n * pattern as `request-cache.ts` and `request-context.ts`).\n *\n * **Tier 2 — per-request.** Wraps both the version read and the defs\n * fetch in `requestCached` so a single page render that hits byline\n * hydration multiple times (e.g. list view + individual byline lookups\n * in a sidebar) pays at most one version read and one defs fetch in\n * total. The defs cache key includes the version, so a (highly\n * unlikely) mid-request bump still produces a self-consistent view —\n * the second call sees a different key and refetches.\n *\n * **Invalidation.** `options.byline_fields_version` is bumped by every\n * `BylineSchemaRegistry` mutation (Phase 2). Each isolate independently\n * reads the persisted version on the next request and compares against\n * its cached version; mismatch triggers a refetch and overwrite. Other\n * isolates see the change within one request after the bump propagates.\n *\n * **Isolated databases bypass the global cache.** Playground and DO\n * preview sessions set `requestContext.dbIsIsolated = true`, signalling\n * the per-request `db` points at an isolated schema that may diverge\n * from the singleton. Schema-derived caches keyed by the singleton's\n * version would silently leak the singleton's defs into the isolated\n * request. We follow the `loader.ts:74` `getTaxonomyNames` precedent:\n * skip both reading from and writing to the global holder when the\n * request is isolated. The per-request cache (`requestCached`) is keyed\n * by the WeakMap'd `EmDashRequestContext`, so it can't cross-pollinate\n * between requests — it stays in play even for isolated DBs.\n *\n * **Why a versioned cache and not a TTL?** The version counter gives\n * deterministic invalidation without the staleness window a TTL would\n * impose. Field-definition changes need to be visible to the next\n * request, not eventually. The cost is one cheap `options` read per\n * request — cheaper than the field-defs fetch it replaces, and cheaper\n * than maintaining a TTL state machine.\n */\n\nimport type { Kysely } from \"kysely\";\n\nimport type { Database } from \"../database/types.js\";\nimport { requestCached } from \"../request-cache.js\";\nimport { getRequestContext } from \"../request-context.js\";\nimport { BylineSchemaRegistry } from \"../schema/byline-registry.js\";\nimport type { BylineFieldDefinition } from \"../schema/types.js\";\n\ninterface FieldDefsHolder {\n\t/** In-flight or resolved defs promise for the cached version. Null until first read. */\n\tcached: Promise<BylineFieldDefinition[]> | null;\n\t/** Persisted-version value that `cached` was fetched against. */\n\tcachedVersion: number;\n}\n\nconst HOLDER_KEY = Symbol.for(\"emdash:byline-field-defs\");\nconst g = globalThis as Record<symbol, unknown>;\nconst holder: FieldDefsHolder =\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- globalThis singleton pattern (see request-cache.ts)\n\t(g[HOLDER_KEY] as FieldDefsHolder | undefined) ??\n\t(() => {\n\t\tconst h: FieldDefsHolder = { cached: null, cachedVersion: -1 };\n\t\tg[HOLDER_KEY] = h;\n\t\treturn h;\n\t})();\n\nconst REQUEST_CACHE_KEY_VERSION = \"byline-fields-version\";\nconst REQUEST_CACHE_KEY_DEFS_PREFIX = \"byline-field-defs:\";\n\n/**\n * Read the persisted `options.byline_fields_version` counter. Cached for\n * the duration of the current request via `requestCached`. Returns `0`\n * when the row is missing (matches `BylineSchemaRegistry.getVersion`).\n */\nasync function getBylineFieldsVersion(db: Kysely<Database>): Promise<number> {\n\treturn requestCached(REQUEST_CACHE_KEY_VERSION, () => new BylineSchemaRegistry(db).getVersion());\n}\n\n/**\n * Resolve registered byline custom-field definitions. Two-tier cache:\n * per-request via `requestCached`, then per-isolate via the global\n * holder.\n *\n * The global holder is bypassed for isolated requests (playground / DO\n * preview, which point at a divergent schema) and for dirty versions\n * (odd counter — see `BylineSchemaRegistry`'s class JSDoc — indicates\n * an in-flight or crashed mutation). Both bypass paths still hit the\n * per-request cache, so a single render dedupes within itself.\n *\n * Always returns an array. Empty = no custom fields registered.\n */\nexport async function getBylineFieldDefs(db: Kysely<Database>): Promise<BylineFieldDefinition[]> {\n\tconst isolated = getRequestContext()?.dbIsIsolated === true;\n\tconst version = await getBylineFieldsVersion(db);\n\tconst dirty = version % 2 !== 0;\n\treturn requestCached(`${REQUEST_CACHE_KEY_DEFS_PREFIX}${version}`, async () => {\n\t\tif (isolated || dirty) {\n\t\t\treturn new BylineSchemaRegistry(db).listFields();\n\t\t}\n\t\tif (holder.cached !== null && holder.cachedVersion === version) {\n\t\t\treturn holder.cached;\n\t\t}\n\t\tconst defs = new BylineSchemaRegistry(db).listFields().catch((error) => {\n\t\t\tif (holder.cached === defs) {\n\t\t\t\tholder.cached = null;\n\t\t\t\tholder.cachedVersion = -1;\n\t\t\t}\n\t\t\tthrow error;\n\t\t});\n\t\tholder.cached = defs;\n\t\tholder.cachedVersion = version;\n\t\treturn defs;\n\t});\n}\n\n/**\n * Test/internal helper: clear the per-isolate cache. Useful for unit\n * tests that mutate the registry directly and need to force a refetch\n * without going through the full version-bump path.\n *\n * Production code paths should rely on the version counter for\n * invalidation — calling this from a write path would bypass the\n * coordination that lets other isolates see the change.\n */\nexport function resetBylineFieldDefsCacheForTests(): void {\n\tholder.cached = null;\n\tholder.cachedVersion = -1;\n}\n","import { sql, type Kysely, type Selectable } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { getBylineFieldDefs } from \"../../bylines/field-defs-cache.js\";\nimport {\n\tclearRequestCacheEntry,\n\tpeekRequestCache,\n\tsetRequestCacheEntry,\n} from \"../../request-cache.js\";\nimport type { BylineFieldDefinition, CustomFieldValue } from \"../../schema/types.js\";\nimport { chunks, SQL_BATCH_SIZE } from \"../../utils/chunks.js\";\nimport { listTablesLike } from \"../dialect-helpers.js\";\nimport { withTransaction } from \"../transaction.js\";\nimport type { BylineTable, Database } from \"../types.js\";\nimport { validateIdentifier } from \"../validate.js\";\nimport {\n\tdecodeCursor,\n\tEmDashValidationError,\n\tencodeCursor,\n\ttype BylineSummary,\n\ttype ContentBylineCredit,\n\ttype FindManyResult,\n} from \"./types.js\";\n\ntype BylineRow = Selectable<BylineTable>;\n\n/**\n * A byline row optionally augmented with the avatar's media columns, folded in\n * by the `LEFT JOIN media` in the content-credit hydration queries. The plain\n * `selectAll()` finders produce rows without these keys, so they're optional\n * and `rowToByline` defaults them to null.\n */\ntype BylineRowWithAvatar = BylineRow & {\n\tavatar_storage_key?: string | null;\n\tavatar_alt?: string | null;\n};\n\nexport interface CreateBylineInput {\n\tslug: string;\n\tdisplayName: string;\n\tbio?: string | null;\n\tavatarMediaId?: string | null;\n\twebsiteUrl?: string | null;\n\tuserId?: string | null;\n\tisGuest?: boolean;\n\t/**\n\t * Locale this byline row belongs to. When omitted, the DB DEFAULT (the\n\t * configured `defaultLocale` after migration 040) is used. Keeps behaviour\n\t * consistent with `TaxonomyRepository.create`.\n\t */\n\tlocale?: string;\n\t/**\n\t * When set, the new row joins the source byline's translation_group rather\n\t * than minting a fresh one. The source must exist; otherwise the create\n\t * throws. Mirrors `TaxonomyRepository.create`.\n\t */\n\ttranslationOf?: string;\n\t/**\n\t * Byline custom-field values to seed on the new row (Phase 6 of\n\t * Discussion #1174). Same semantics as `UpdateBylineInput.customFields`:\n\t * keys must match registered slugs in `_emdash_byline_fields`, values\n\t * are validated against the field's type, and writes route to\n\t * `_emdash_byline_field_values` (translatable) or\n\t * `_emdash_byline_field_group_values` (group-shared). Validation runs\n\t * before the row insert so a bad value can't leave a bare byline behind.\n\t */\n\tcustomFields?: Record<string, unknown>;\n}\n\nexport interface UpdateBylineInput {\n\tslug?: string;\n\tdisplayName?: string;\n\tbio?: string | null;\n\tavatarMediaId?: string | null;\n\twebsiteUrl?: string | null;\n\tuserId?: string | null;\n\tisGuest?: boolean;\n\t/**\n\t * Byline custom-field values to write (Phase 3 of Discussion #1174).\n\t *\n\t * Each key must match a registered slug in `_emdash_byline_fields`;\n\t * unknown keys throw `EmDashValidationError`. Per-field writes route\n\t * to `_emdash_byline_field_values` (when the field's `translatable`\n\t * flag is true) or `_emdash_byline_field_group_values` (when false).\n\t * A value of `null` clears the row.\n\t *\n\t * Values are validated against the field's type:\n\t * - `string` / `text` / `url` accept a `string`\n\t * - `boolean` accepts a `boolean`\n\t * - `select` accepts a `string` that appears in `validation.options`\n\t *\n\t * Writes are idempotent (`INSERT … ON CONFLICT DO UPDATE`), so\n\t * retrying the same update produces the same DB state.\n\t */\n\tcustomFields?: Record<string, unknown>;\n}\n\nexport interface ContentBylineInput {\n\tbylineId: string;\n\troleLabel?: string | null;\n}\n\nfunction rowToByline(row: BylineRowWithAvatar): BylineSummary {\n\treturn {\n\t\tid: row.id,\n\t\tslug: row.slug,\n\t\tdisplayName: row.display_name,\n\t\tbio: row.bio,\n\t\tavatarMediaId: row.avatar_media_id,\n\t\tavatarStorageKey: row.avatar_storage_key ?? null,\n\t\tavatarAlt: row.avatar_alt ?? null,\n\t\twebsiteUrl: row.website_url,\n\t\tuserId: row.user_id,\n\t\tisGuest: row.is_guest === 1,\n\t\tcreatedAt: row.created_at,\n\t\tupdatedAt: row.updated_at,\n\t\tlocale: row.locale,\n\t\ttranslationGroup: row.translation_group,\n\t};\n}\n\n/**\n * Merge a single decoded value into a `BylineSummary.customFields` map.\n * Centralised so the merge semantics (null storage, JSON.parse failure\n * handling) live in one place across both translatable and group-shared\n * paths.\n *\n * A stored row with `value = NULL` (representing an explicit null) is\n * surfaced as `null` in `customFields`. A row with a malformed JSON\n * payload is dropped silently with a `console.warn` — a corrupted\n * payload shouldn't break the entire byline hydration; the field-defs\n * cache will let admins replace the value, and the warning makes the\n * issue debuggable. (Storage path uses `JSON.stringify`, so the only\n * way to get malformed JSON is direct DB tampering or a future\n * migration bug.)\n */\nfunction assignCustomFieldValue(\n\tsummary: BylineSummary,\n\tfield: BylineFieldDefinition,\n\tstored: string | null,\n): void {\n\tconst target = summary.customFields ?? {};\n\tif (stored === null) {\n\t\ttarget[field.slug] = null;\n\t} else {\n\t\ttry {\n\t\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- coerceFieldValue ran at write time, see field-defs-cache.ts\n\t\t\ttarget[field.slug] = JSON.parse(stored) as CustomFieldValue;\n\t\t} catch {\n\t\t\tconsole.warn(\n\t\t\t\t`[BylineRepository] dropping malformed JSON for byline=${summary.id} ` +\n\t\t\t\t\t`field=${field.slug}: ${stored.slice(0, 60)}`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t}\n\tsummary.customFields = target;\n}\n\n/**\n * Coerce a raw write-path value to `CustomFieldValue`, throwing\n * `EmDashValidationError` on type mismatch. `null` clears the field\n * (DELETE in the write path).\n *\n * TODO: `field.required` is not enforced. The admin UI exposes the\n * toggle but the backend accepts missing values; design pass needed\n * on the enforcement model.\n */\nfunction coerceFieldValue(field: BylineFieldDefinition, raw: unknown): CustomFieldValue {\n\tif (raw === null) return null;\n\n\tswitch (field.type) {\n\t\tcase \"string\":\n\t\tcase \"text\": {\n\t\t\tif (typeof raw !== \"string\") {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Byline field \"${field.slug}\" expects a string value (received ${typeof raw})`,\n\t\t\t\t\t{ slug: field.slug, type: field.type, received: typeof raw },\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn raw;\n\t\t}\n\t\tcase \"url\": {\n\t\t\tif (typeof raw !== \"string\") {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Byline field \"${field.slug}\" expects a string value (received ${typeof raw})`,\n\t\t\t\t\t{ slug: field.slug, type: field.type, received: typeof raw },\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Empty string round-trips as a clear from the admin UI; any\n\t\t\t// non-empty value must be a valid http(s) URL. The scheme\n\t\t\t// allowlist mirrors `httpUrl` in `api/schemas/common.ts` —\n\t\t\t// `new URL` alone would accept `javascript:`/`data:` etc.\n\t\t\tif (raw === \"\") return raw;\n\t\t\tlet parsed: URL;\n\t\t\ttry {\n\t\t\t\tparsed = new URL(raw);\n\t\t\t} catch {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Byline field \"${field.slug}\" expects a valid URL (received \"${raw}\")`,\n\t\t\t\t\t{ slug: field.slug, type: field.type, received: raw },\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Byline field \"${field.slug}\" must use http or https scheme (received \"${parsed.protocol}\")`,\n\t\t\t\t\t{ slug: field.slug, type: field.type, received: raw, protocol: parsed.protocol },\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn raw;\n\t\t}\n\t\tcase \"boolean\": {\n\t\t\tif (typeof raw !== \"boolean\") {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Byline field \"${field.slug}\" expects a boolean value (received ${typeof raw})`,\n\t\t\t\t\t{ slug: field.slug, type: field.type, received: typeof raw },\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn raw;\n\t\t}\n\t\tcase \"select\": {\n\t\t\tif (typeof raw !== \"string\") {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Byline field \"${field.slug}\" expects a string value (received ${typeof raw})`,\n\t\t\t\t\t{ slug: field.slug, type: field.type, received: typeof raw },\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst options = field.validation?.options ?? [];\n\t\t\tif (!options.includes(raw)) {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Byline field \"${field.slug}\" value \"${raw}\" is not one of the registered choices`,\n\t\t\t\t\t{ slug: field.slug, value: raw, options },\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn raw;\n\t\t}\n\t}\n}\n\n/**\n * Byline repository for content credits.\n *\n * Bylines are per-locale (migration 040). Translations of the same byline\n * share a `translation_group` ULID. `_emdash_content_bylines.byline_id` and\n * `ec_*.primary_byline_id` store the translation_group (not a row id) so a\n * single credit spans every locale variant of a byline.\n *\n * The repository does not resolve locale fallbacks on its own — callers\n * supply the locale they want. Hydration is strict per locale: a credit at\n * locale X renders iff a byline row exists at locale X within the credited\n * translation group. This mirrors `TaxonomyRepository.getTermsForEntry` and\n * the convention established by PR #916.\n *\n * Runtime helpers in `packages/core/src/bylines/index.ts` may layer fallback\n * resolution on top for the \"look up one byline by slug\" path, but the\n * relation-hydration methods on this class are always strict.\n */\nexport class BylineRepository {\n\tconstructor(private db: Kysely<Database>) {}\n\n\t// ============================================\n\t// Custom-field hydration (Phase 3 of #1174)\n\t// ============================================\n\n\t/**\n\t * Merge `customFields` onto each `BylineSummary` produced from the\n\t * given rows. Two batched queries total — one against\n\t * `_emdash_byline_field_values` (keyed by `byline_id`), one against\n\t * `_emdash_byline_field_group_values` (keyed by `translation_group`)\n\t * — both chunked at `SQL_BATCH_SIZE` for D1's bound-parameter cap.\n\t *\n\t * When zero fields are registered, every row gets `customFields = {}`\n\t * with no value-table reads (the field-defs cache returns `[]`).\n\t * Group-shared values are looked up via the row's `translation_group`,\n\t * so every locale sibling of the same byline identity sees the same\n\t * non-translatable value without re-reading per row.\n\t *\n\t * **Duplicate-row handling.** Callers (notably `getContentBylinesMany`\n\t * for list views with repeated authors) can pass the same byline row\n\t * multiple times. We assign values by *iterating both `rows` and\n\t * `summaries` in lockstep by index*, not by deduping into a Map keyed\n\t * on byline id. A Map approach silently drops earlier duplicates' merge\n\t * step (last writer wins, earlier instances keep their initial `{}`).\n\t * Iterating by index gives every duplicate its own merged copy.\n\t *\n\t * Hydration is *strict per row* — values are merged onto whichever\n\t * `BylineRow` produced them. Fallback semantics (e.g. \"if no value\n\t * for this locale, show the default-locale value\") are not the\n\t * repository's concern; consumers layer them on top if wanted, the\n\t * same way `BylineRepository` doesn't resolve locale fallback for\n\t * the base byline lookup.\n\t */\n\tprivate async withCustomFields(rows: BylineRow[]): Promise<BylineSummary[]> {\n\t\tconst summaries = rows.map(rowToByline);\n\t\t// Always populate `customFields = {}` (PR plan AC #6) — even when\n\t\t// no fields are registered, every BylineSummary carries the empty\n\t\t// object. A fresh object per summary so duplicate rows don't share\n\t\t// state.\n\t\tfor (const summary of summaries) {\n\t\t\tsummary.customFields = {};\n\t\t}\n\t\tawait this.applyCustomFieldsTo(summaries);\n\t\treturn summaries;\n\t}\n\n\tprivate async withCustomFieldsOne(row: BylineRow | undefined): Promise<BylineSummary | null> {\n\t\tif (!row) return null;\n\t\tconst [result] = await this.withCustomFields([row]);\n\t\treturn result ?? null;\n\t}\n\n\t/**\n\t * Hydrate `customFields` on each `BylineSummary`, mutating in place.\n\t *\n\t * The public entry point for callers that fetch byline rows in\n\t * multiple passes (e.g. `getBylinesForEntries`, which buckets by\n\t * locale and calls `getContentBylinesMany` per bucket) and want a\n\t * single batched hydration over the union of bylines, not one per\n\t * pass. Use with the `skipHydration` option on the read methods to\n\t * defer customFields work to a single call here.\n\t *\n\t * Two batched queries total (translatable + group-shared) regardless\n\t * of how many bylines, locales, or translation_groups are in the\n\t * input — meets the Phase 3 query-count envelope for mixed-locale\n\t * list views even when sibling locales reference disjoint\n\t * translation_groups.\n\t *\n\t * Replaces any existing `customFields` on each summary with a freshly\n\t * fetched map. Callers that want to merge rather than replace should\n\t * not use this entry point.\n\t */\n\tasync hydrateBylineCustomFields(summaries: BylineSummary[]): Promise<void> {\n\t\tfor (const summary of summaries) {\n\t\t\tsummary.customFields = {};\n\t\t}\n\t\tawait this.applyCustomFieldsTo(summaries);\n\t}\n\n\t/**\n\t * Shared merge engine for `withCustomFields` and\n\t * `hydrateBylineCustomFields`. Reads field defs (cached), batches the\n\t * translatable + group-shared fetches, and walks `summaries` directly\n\t * to apply values.\n\t *\n\t * Iterates `summaries` (not a `summaryById` map) so duplicate\n\t * `BylineSummary` objects sharing the same `id` — e.g. the same\n\t * author credited to multiple entries — each get their own merged\n\t * values. The previous Map-based dedup silently dropped earlier\n\t * duplicates' merge step.\n\t */\n\tprivate async applyCustomFieldsTo(summaries: BylineSummary[]): Promise<void> {\n\t\tif (summaries.length === 0) return;\n\n\t\tconst defs = await getBylineFieldDefs(this.db);\n\t\tif (defs.length === 0) return;\n\n\t\tconst fieldById = new Map(defs.map((d) => [d.id, d]));\n\n\t\t// Translatable values, batched by byline_id (unique per locale, so\n\t\t// IDs across different locale buckets don't collide — one batched\n\t\t// query covers everything).\n\t\tconst translatableByByline = new Map<string, Map<string, string | null>>();\n\t\tconst bylineIds = [...new Set(summaries.map((s) => s.id))];\n\t\tfor (const chunk of chunks(bylineIds, SQL_BATCH_SIZE)) {\n\t\t\tconst trRows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_byline_field_values\")\n\t\t\t\t.select([\"byline_id\", \"field_id\", \"value\"])\n\t\t\t\t.where(\"byline_id\", \"in\", chunk)\n\t\t\t\t.execute();\n\t\t\tfor (const trRow of trRows) {\n\t\t\t\tlet fieldMap = translatableByByline.get(trRow.byline_id);\n\t\t\t\tif (!fieldMap) {\n\t\t\t\t\tfieldMap = new Map();\n\t\t\t\t\ttranslatableByByline.set(trRow.byline_id, fieldMap);\n\t\t\t\t}\n\t\t\t\tfieldMap.set(trRow.field_id, trRow.value);\n\t\t\t}\n\t\t}\n\n\t\t// Group-shared values, batched over the union of translation_groups,\n\t\t// with per-group request-cache priming so subsequent calls within\n\t\t// the same request share the lookup. Together with the\n\t\t// `hydrateBylineCustomFields` + `skipHydration` flow in\n\t\t// `getBylinesForEntries`, this keeps mixed-locale list views to\n\t\t// **one** group-shared query per request, even for disjoint\n\t\t// translation_groups across locale buckets.\n\t\tconst groups = [\n\t\t\t...new Set(\n\t\t\t\tsummaries\n\t\t\t\t\t.map((s) => s.translationGroup)\n\t\t\t\t\t.filter((g): g is string => typeof g === \"string\" && g.length > 0),\n\t\t\t),\n\t\t];\n\t\tconst groupByGroup = await this.loadGroupValuesByIds(groups);\n\n\t\t// Each loop gates on `field.translatable` so a row in the wrong\n\t\t// owner table (e.g. left over from a translatable flip) can't\n\t\t// leak into hydration.\n\t\tfor (const summary of summaries) {\n\t\t\tconst trValues = translatableByByline.get(summary.id);\n\t\t\tif (trValues) {\n\t\t\t\tfor (const [fieldId, value] of trValues) {\n\t\t\t\t\tconst field = fieldById.get(fieldId);\n\t\t\t\t\tif (!field || !field.translatable) continue;\n\t\t\t\t\tassignCustomFieldValue(summary, field, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (summary.translationGroup) {\n\t\t\t\tconst grpValues = groupByGroup.get(summary.translationGroup);\n\t\t\t\tif (grpValues) {\n\t\t\t\t\tfor (const [fieldId, value] of grpValues) {\n\t\t\t\t\t\tconst field = fieldById.get(fieldId);\n\t\t\t\t\t\tif (!field || field.translatable) continue;\n\t\t\t\t\t\tassignCustomFieldValue(summary, field, value);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the group-shared custom-field values for a set of\n\t * translation_groups, sharing work across hydration calls within the\n\t * same request via per-group `requestCached` entries.\n\t *\n\t * The non-translatable storage table (`_emdash_byline_field_group_values`)\n\t * is keyed by `translation_group`, which is locale-agnostic. Combining\n\t * this method with `skipHydration` on `getContentBylinesMany` and a\n\t * single `hydrateBylineCustomFields` call (see\n\t * `getBylinesForEntries`) keeps mixed-locale list hydration to **one**\n\t * batched group-shared SQL per request — even with disjoint\n\t * translation_groups across locale buckets. Solo callers (`findById`,\n\t * `findMany`, etc.) still get the same per-call batching they had\n\t * before; the cache simply means a second call in the same request\n\t * for an overlapping group is free.\n\t *\n\t * Cache key: `byline-field-group-values:${groupId}` — one entry per\n\t * group. Writes use `setRequestCacheEntry` (idempotent, doesn't\n\t * overwrite); `BylineRepository.update` calls `clearRequestCacheEntry`\n\t * after a group-shared write to keep the cache fresh within the same\n\t * request.\n\t */\n\tprivate async loadGroupValuesByIds(\n\t\tgroups: string[],\n\t): Promise<Map<string, Map<string, string | null>>> {\n\t\tconst result = new Map<string, Map<string, string | null>>();\n\t\tif (groups.length === 0) return result;\n\n\t\t// First pass: pull any already-cached groups from the request scope.\n\t\tconst missing: string[] = [];\n\t\tfor (const g of groups) {\n\t\t\tconst cached = peekRequestCache<Map<string, string | null>>(`byline-field-group-values:${g}`);\n\t\t\tif (cached) {\n\t\t\t\tresult.set(g, await cached);\n\t\t\t} else {\n\t\t\t\tmissing.push(g);\n\t\t\t}\n\t\t}\n\n\t\tif (missing.length === 0) return result;\n\n\t\t// Second pass: one batched SQL for the union of all missing groups\n\t\t// (chunked for D1's bound-parameter cap). Initialise empty maps for\n\t\t// missing groups so the primed cache covers \"this group has no\n\t\t// values\" — preventing a re-fetch on subsequent calls.\n\t\tconst fetched = new Map<string, Map<string, string | null>>();\n\t\tfor (const g of missing) fetched.set(g, new Map());\n\t\tfor (const chunk of chunks(missing, SQL_BATCH_SIZE)) {\n\t\t\tconst grpRows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_byline_field_group_values\")\n\t\t\t\t.select([\"translation_group\", \"field_id\", \"value\"])\n\t\t\t\t.where(\"translation_group\", \"in\", chunk)\n\t\t\t\t.execute();\n\t\t\tfor (const grpRow of grpRows) {\n\t\t\t\tconst fieldMap = fetched.get(grpRow.translation_group);\n\t\t\t\tif (!fieldMap) continue;\n\t\t\t\tfieldMap.set(grpRow.field_id, grpRow.value);\n\t\t\t}\n\t\t}\n\n\t\tfor (const g of missing) {\n\t\t\tconst m = fetched.get(g);\n\t\t\tif (!m) continue;\n\t\t\tsetRequestCacheEntry(`byline-field-group-values:${g}`, m);\n\t\t\tresult.set(g, m);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t// ============================================\n\t// Reads\n\t// ============================================\n\n\tasync findById(id: string): Promise<BylineSummary | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\t\treturn this.withCustomFieldsOne(row);\n\t}\n\n\t/**\n\t * Find a byline by slug. When `locale` is provided, filter by it strictly.\n\t * When omitted, returns the lowest-locale-code match (deterministic across\n\t * calls). Mirrors `TaxonomyRepository.findBySlug`.\n\t */\n\tasync findBySlug(slug: string, options?: { locale?: string }): Promise<BylineSummary | null> {\n\t\tlet query = this.db.selectFrom(\"_emdash_bylines\").selectAll().where(\"slug\", \"=\", slug);\n\t\tif (options?.locale !== undefined) query = query.where(\"locale\", \"=\", options.locale);\n\t\tconst row = await query.orderBy(\"locale\", \"asc\").executeTakeFirst();\n\t\treturn this.withCustomFieldsOne(row);\n\t}\n\n\t/**\n\t * Find the byline linked to a CMS user. Post-migration 040 the partial\n\t * unique on user_id is `(user_id, locale)`, so `locale` is required to\n\t * disambiguate when multiple locale variants exist. When omitted, returns\n\t * the lowest-locale-code match.\n\t */\n\tasync findByUserId(userId: string, options?: { locale?: string }): Promise<BylineSummary | null> {\n\t\tlet query = this.db.selectFrom(\"_emdash_bylines\").selectAll().where(\"user_id\", \"=\", userId);\n\t\tif (options?.locale !== undefined) query = query.where(\"locale\", \"=\", options.locale);\n\t\tconst row = await query.orderBy(\"locale\", \"asc\").executeTakeFirst();\n\t\treturn this.withCustomFieldsOne(row);\n\t}\n\n\tasync findMany(options?: {\n\t\tsearch?: string;\n\t\tisGuest?: boolean;\n\t\tuserId?: string;\n\t\tlocale?: string;\n\t\tcursor?: string;\n\t\tlimit?: number;\n\t}): Promise<FindManyResult<BylineSummary>> {\n\t\tconst limit = Math.min(Math.max(options?.limit ?? 50, 1), 100);\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(\"created_at\", \"desc\")\n\t\t\t.orderBy(\"id\", \"desc\")\n\t\t\t.limit(limit + 1);\n\n\t\tif (options?.search) {\n\t\t\tconst escaped = options.search\n\t\t\t\t.replaceAll(\"\\\\\", \"\\\\\\\\\")\n\t\t\t\t.replaceAll(\"%\", \"\\\\%\")\n\t\t\t\t.replaceAll(\"_\", \"\\\\_\");\n\t\t\tconst term = `%${escaped}%`;\n\t\t\tquery = query.where((eb) =>\n\t\t\t\teb.or([eb(\"display_name\", \"like\", term), eb(\"slug\", \"like\", term)]),\n\t\t\t);\n\t\t}\n\n\t\tif (options?.isGuest !== undefined) {\n\t\t\tquery = query.where(\"is_guest\", \"=\", options.isGuest ? 1 : 0);\n\t\t}\n\n\t\tif (options?.userId !== undefined) {\n\t\t\tquery = query.where(\"user_id\", \"=\", options.userId);\n\t\t}\n\n\t\tif (options?.locale !== undefined) {\n\t\t\tquery = query.where(\"locale\", \"=\", options.locale);\n\t\t}\n\n\t\tif (options?.cursor) {\n\t\t\tconst decoded = decodeCursor(options.cursor);\n\t\t\tquery = query.where((eb) =>\n\t\t\t\teb.or([\n\t\t\t\t\teb(\"created_at\", \"<\", decoded.orderValue),\n\t\t\t\t\teb.and([eb(\"created_at\", \"=\", decoded.orderValue), eb(\"id\", \"<\", decoded.id)]),\n\t\t\t\t]),\n\t\t\t);\n\t\t}\n\n\t\tconst rows = await query.execute();\n\t\tconst pageRows = rows.slice(0, limit);\n\t\tconst items = await this.withCustomFields(pageRows);\n\t\tconst result: FindManyResult<BylineSummary> = { items };\n\n\t\tif (rows.length > limit) {\n\t\t\tconst last = items.at(-1);\n\t\t\tif (last) {\n\t\t\t\tresult.nextCursor = encodeCursor(last.createdAt, last.id);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * List every sibling row in `translation_group`. Used by the admin\n\t * `TranslationsPanel` to render one entry per configured locale.\n\t */\n\tasync listTranslations(id: string): Promise<BylineSummary[]> {\n\t\tconst anchor = await this.findById(id);\n\t\tif (!anchor) return [];\n\t\tconst group = anchor.translationGroup ?? anchor.id;\n\t\treturn this.findByTranslationGroup(group);\n\t}\n\n\t/**\n\t * Direct lookup by `translation_group`. Returns every locale variant of a\n\t * byline, ordered by locale code (deterministic).\n\t */\n\tasync findByTranslationGroup(translationGroup: string): Promise<BylineSummary[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t.selectAll()\n\t\t\t.where(\"translation_group\", \"=\", translationGroup)\n\t\t\t.orderBy(\"locale\", \"asc\")\n\t\t\t.execute();\n\t\treturn this.withCustomFields(rows);\n\t}\n\n\t/**\n\t * Validate a `customFields` input map into a write list before any row\n\t * write — throws `EmDashValidationError` on unknown slugs, type\n\t * mismatches, or select-choice misses.\n\t */\n\tprivate async resolveCustomFieldWrites(\n\t\tcustomFields: Record<string, unknown> | undefined,\n\t): Promise<Array<{ field: BylineFieldDefinition; value: CustomFieldValue }>> {\n\t\tif (!customFields || Object.keys(customFields).length === 0) return [];\n\t\tconst defs = await getBylineFieldDefs(this.db);\n\t\tconst bySlug = new Map(defs.map((d) => [d.slug, d]));\n\t\tconst writes: Array<{ field: BylineFieldDefinition; value: CustomFieldValue }> = [];\n\t\tfor (const [slug, raw] of Object.entries(customFields)) {\n\t\t\tconst field = bySlug.get(slug);\n\t\t\tif (!field) {\n\t\t\t\tthrow new EmDashValidationError(`Unknown byline custom field \"${slug}\"`, {\n\t\t\t\t\tslug,\n\t\t\t\t\tregistered: defs.map((d) => d.slug),\n\t\t\t\t});\n\t\t\t}\n\t\t\twrites.push({ field, value: coerceFieldValue(field, raw) });\n\t\t}\n\t\treturn writes;\n\t}\n\n\t/**\n\t * Write a validated custom-field list against a byline row inside the\n\t * caller's transaction. Per-field writes route to\n\t * `_emdash_byline_field_values` (translatable) or\n\t * `_emdash_byline_field_group_values` (group-shared); `null` clears.\n\t * Returns `true` when any group-shared row was touched so the caller\n\t * can invalidate the per-request cache post-commit.\n\t */\n\tprivate async applyCustomFieldWritesInTrx(\n\t\ttrx: Kysely<Database>,\n\t\tbylineId: string,\n\t\ttranslationGroup: string,\n\t\twrites: Array<{ field: BylineFieldDefinition; value: CustomFieldValue }>,\n\t\tnow: string,\n\t): Promise<boolean> {\n\t\tif (writes.length === 0) return false;\n\t\tlet touchedGroupShared = false;\n\t\tfor (const { field, value } of writes) {\n\t\t\tif (!field.translatable) touchedGroupShared = true;\n\t\t\tif (field.translatable) {\n\t\t\t\tif (value === null) {\n\t\t\t\t\tawait trx\n\t\t\t\t\t\t.deleteFrom(\"_emdash_byline_field_values\")\n\t\t\t\t\t\t.where(\"byline_id\", \"=\", bylineId)\n\t\t\t\t\t\t.where(\"field_id\", \"=\", field.id)\n\t\t\t\t\t\t.execute();\n\t\t\t\t} else {\n\t\t\t\t\tconst encoded = JSON.stringify(value);\n\t\t\t\t\tawait trx\n\t\t\t\t\t\t.insertInto(\"_emdash_byline_field_values\")\n\t\t\t\t\t\t.values({\n\t\t\t\t\t\t\tbyline_id: bylineId,\n\t\t\t\t\t\t\tfield_id: field.id,\n\t\t\t\t\t\t\tvalue: encoded,\n\t\t\t\t\t\t\tcreated_at: now,\n\t\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.onConflict((oc) =>\n\t\t\t\t\t\t\toc.columns([\"byline_id\", \"field_id\"]).doUpdateSet({\n\t\t\t\t\t\t\t\tvalue: encoded,\n\t\t\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.execute();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (value === null) {\n\t\t\t\t\tawait trx\n\t\t\t\t\t\t.deleteFrom(\"_emdash_byline_field_group_values\")\n\t\t\t\t\t\t.where(\"translation_group\", \"=\", translationGroup)\n\t\t\t\t\t\t.where(\"field_id\", \"=\", field.id)\n\t\t\t\t\t\t.execute();\n\t\t\t\t} else {\n\t\t\t\t\tconst encoded = JSON.stringify(value);\n\t\t\t\t\tawait trx\n\t\t\t\t\t\t.insertInto(\"_emdash_byline_field_group_values\")\n\t\t\t\t\t\t.values({\n\t\t\t\t\t\t\ttranslation_group: translationGroup,\n\t\t\t\t\t\t\tfield_id: field.id,\n\t\t\t\t\t\t\tvalue: encoded,\n\t\t\t\t\t\t\tcreated_at: now,\n\t\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.onConflict((oc) =>\n\t\t\t\t\t\t\toc.columns([\"translation_group\", \"field_id\"]).doUpdateSet({\n\t\t\t\t\t\t\t\tvalue: encoded,\n\t\t\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.execute();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn touchedGroupShared;\n\t}\n\n\tasync create(input: CreateBylineInput): Promise<BylineSummary> {\n\t\tconst id = ulid();\n\t\tconst now = new Date().toISOString();\n\n\t\t// Validate customFields before opening the transaction so a bad\n\t\t// value surfaces as VALIDATION_ERROR without aborting an insert.\n\t\tconst customFieldWrites = await this.resolveCustomFieldWrites(input.customFields);\n\n\t\t// translationOf joins the source's group; otherwise mint a fresh\n\t\t// group = id (matches migration 040's backfill pattern).\n\t\tlet translationGroup: string = id;\n\t\tif (input.translationOf) {\n\t\t\tconst source = await this.findById(input.translationOf);\n\t\t\tif (!source) throw new Error(\"Source byline for translation not found\");\n\t\t\ttranslationGroup = source.translationGroup ?? source.id;\n\t\t}\n\n\t\t// Wrap insert + custom-field writes in one transaction so a\n\t\t// partial failure rolls both back on Node/PG. D1 still has its\n\t\t// own no-transactions limitation — recovery for that path lives\n\t\t// in `handleBylineCreate`.\n\t\tlet touchedGroupShared = false;\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tawait trx\n\t\t\t\t.insertInto(\"_emdash_bylines\")\n\t\t\t\t.values({\n\t\t\t\t\tid,\n\t\t\t\t\tslug: input.slug,\n\t\t\t\t\tdisplay_name: input.displayName,\n\t\t\t\t\tbio: input.bio ?? null,\n\t\t\t\t\tavatar_media_id: input.avatarMediaId ?? null,\n\t\t\t\t\twebsite_url: input.websiteUrl ?? null,\n\t\t\t\t\tuser_id: input.userId ?? null,\n\t\t\t\t\tis_guest: input.isGuest ? 1 : 0,\n\t\t\t\t\tcreated_at: now,\n\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t// Omit `locale` so the DB DEFAULT (configured defaultLocale)\n\t\t\t\t\t// applies — matches TaxonomyRepository.create.\n\t\t\t\t\t...(input.locale !== undefined ? { locale: input.locale } : {}),\n\t\t\t\t\ttranslation_group: translationGroup,\n\t\t\t\t})\n\t\t\t\t.execute();\n\n\t\t\ttouchedGroupShared = await this.applyCustomFieldWritesInTrx(\n\t\t\t\ttrx,\n\t\t\t\tid,\n\t\t\t\ttranslationGroup,\n\t\t\t\tcustomFieldWrites,\n\t\t\t\tnow,\n\t\t\t);\n\t\t});\n\n\t\tif (touchedGroupShared) {\n\t\t\tclearRequestCacheEntry(`byline-field-group-values:${translationGroup}`);\n\t\t}\n\n\t\tconst byline = await this.findById(id);\n\t\tif (!byline) {\n\t\t\tthrow new Error(\"Failed to create byline\");\n\t\t}\n\t\treturn byline;\n\t}\n\n\tasync update(id: string, input: UpdateBylineInput): Promise<BylineSummary | null> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) return null;\n\n\t\t// Validate customFields before opening the transaction so a bad\n\t\t// value surfaces as VALIDATION_ERROR without aborting an update.\n\t\tconst customFieldWrites = await this.resolveCustomFieldWrites(input.customFields);\n\n\t\tconst now = new Date().toISOString();\n\t\tconst updates: Record<string, unknown> = { updated_at: now };\n\n\t\tif (input.slug !== undefined) updates.slug = input.slug;\n\t\tif (input.displayName !== undefined) updates.display_name = input.displayName;\n\t\tif (input.bio !== undefined) updates.bio = input.bio;\n\t\tif (input.avatarMediaId !== undefined) updates.avatar_media_id = input.avatarMediaId;\n\t\tif (input.websiteUrl !== undefined) updates.website_url = input.websiteUrl;\n\t\tif (input.userId !== undefined) updates.user_id = input.userId;\n\t\tif (input.isGuest !== undefined) updates.is_guest = input.isGuest ? 1 : 0;\n\n\t\tconst group = existing.translationGroup ?? existing.id;\n\t\t// Wrap row update + custom-field writes in one transaction so a\n\t\t// partial failure rolls both back on Node/PG. The post-commit\n\t\t// invalidation below clears the per-request cache that the\n\t\t// top-of-method `findById` populated for this group.\n\t\tlet touchedGroupShared = false;\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tawait trx.updateTable(\"_emdash_bylines\").set(updates).where(\"id\", \"=\", id).execute();\n\t\t\ttouchedGroupShared = await this.applyCustomFieldWritesInTrx(\n\t\t\t\ttrx,\n\t\t\t\tid,\n\t\t\t\tgroup,\n\t\t\t\tcustomFieldWrites,\n\t\t\t\tnow,\n\t\t\t);\n\t\t});\n\n\t\tif (touchedGroupShared) {\n\t\t\tclearRequestCacheEntry(`byline-field-group-values:${group}`);\n\t\t}\n\n\t\treturn await this.findById(id);\n\t}\n\n\t/**\n\t * Delete a byline row. When this row is the last sibling in its\n\t * translation group, also drops every junction row pointing at the group,\n\t * clears `primary_byline_id` references, and removes the byline's\n\t * non-translatable custom-field values. When other siblings remain in\n\t * the group, junctions, `primary_byline_id` pointers, and group-shared\n\t * custom-field values stay intact — the credit (and its shared metadata)\n\t * lives on at other locales.\n\t *\n\t * **Application-level cascade.** The byline domain has standardised on\n\t * app-level cascade rather than trusting FK ON DELETE CASCADE, partly\n\t * because migration 040 had to strip its own FK to support the\n\t * translation_group remap (#1021), and partly so cleanup doesn't\n\t * depend on `PRAGMA foreign_keys = ON` (set in production via\n\t * `connection.ts:60`, but easy to bypass in tests, scripts, and\n\t * one-off tools). Every byline-related deletion table is cleared\n\t * explicitly here:\n\t *\n\t * - `_emdash_byline_field_values` (per-byline translatable values) —\n\t * migration 041 declares FK ON DELETE CASCADE on `byline_id`; the\n\t * explicit DELETE removes the dependency on that pragma.\n\t * - `_emdash_content_bylines` — migration 040 dropped its FK.\n\t * - `ec_*.primary_byline_id` — never had an FK.\n\t * - `_emdash_byline_field_group_values` (translation-group-keyed) —\n\t * keyed by a text column with no FK to bylines, so app-level cleanup\n\t * is the only path.\n\t *\n\t * The FKs that remain (migration 041) serve as defense-in-depth.\n\t */\n\tasync delete(id: string): Promise<boolean> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) return false;\n\n\t\tconst group = existing.translationGroup ?? existing.id;\n\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\t// Per-row translatable custom-field values. Done BEFORE the\n\t\t\t// byline row delete so the application-level cleanup is\n\t\t\t// observable in the transaction log even if FK enforcement is\n\t\t\t// off; migration 041's FK ON DELETE CASCADE would catch any\n\t\t\t// row we miss, but the explicit DELETE is what the rest of\n\t\t\t// the byline domain expects to see.\n\t\t\tawait trx.deleteFrom(\"_emdash_byline_field_values\").where(\"byline_id\", \"=\", id).execute();\n\n\t\t\tawait trx.deleteFrom(\"_emdash_bylines\").where(\"id\", \"=\", id).execute();\n\n\t\t\t// Count remaining siblings in the translation group. If none\n\t\t\t// remain, purge dependent rows; otherwise leave them intact so\n\t\t\t// the credit still resolves at other locales.\n\t\t\tconst remaining = await trx\n\t\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t\t.select(({ fn }) => [fn.count<number>(\"id\").as(\"count\")])\n\t\t\t\t.where(\"translation_group\", \"=\", group)\n\t\t\t\t.executeTakeFirst();\n\t\t\tconst remainingCount = Number(remaining?.count ?? 0);\n\t\t\tif (remainingCount > 0) return;\n\n\t\t\t// Last sibling gone: cascade in application code.\n\t\t\tawait trx.deleteFrom(\"_emdash_content_bylines\").where(\"byline_id\", \"=\", group).execute();\n\n\t\t\t// Group-shared custom-field values are keyed by translation_group\n\t\t\t// (no FK to bylines), so they don't cascade with the byline row.\n\t\t\t// Clean them up explicitly so deleting the last sibling of an\n\t\t\t// identity doesn't leave orphan group values pointing at a\n\t\t\t// vanished translation group. Per-row translatable values\n\t\t\t// (`_emdash_byline_field_values` keyed by byline_id) already\n\t\t\t// cascaded when each sibling row was deleted, so no extra\n\t\t\t// cleanup is needed for that table.\n\t\t\tawait trx\n\t\t\t\t.deleteFrom(\"_emdash_byline_field_group_values\")\n\t\t\t\t.where(\"translation_group\", \"=\", group)\n\t\t\t\t.execute();\n\n\t\t\tconst tableNames = await listTablesLike(trx, \"ec_%\");\n\t\t\tfor (const tableName of tableNames) {\n\t\t\t\tvalidateIdentifier(tableName, \"content table\");\n\t\t\t\tawait sql`\n\t\t\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\t\t\tSET primary_byline_id = NULL\n\t\t\t\t\tWHERE primary_byline_id = ${group}\n\t\t\t\t`.execute(trx);\n\t\t\t}\n\t\t});\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Strict per-locale credit hydration. Joins `_emdash_content_bylines` to\n\t * `_emdash_bylines` on `translation_group = byline_id`, then filters to\n\t * the requested locale. Credits whose translation group lacks a row at\n\t * the requested locale are omitted — callers wanting fallback behaviour\n\t * apply it themselves. Mirrors `TaxonomyRepository.getTermsForEntry`.\n\t */\n\tasync getContentBylines(\n\t\tcollectionSlug: string,\n\t\tcontentId: string,\n\t\toptions?: { locale?: string },\n\t): Promise<ContentBylineCredit[]> {\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_content_bylines as cb\")\n\t\t\t.innerJoin(\"_emdash_bylines as b\", \"b.translation_group\", \"cb.byline_id\")\n\t\t\t.leftJoin(\"media as m\", \"m.id\", \"b.avatar_media_id\")\n\t\t\t.select([\n\t\t\t\t\"cb.sort_order as sort_order\",\n\t\t\t\t\"cb.role_label as role_label\",\n\t\t\t\t\"b.id as id\",\n\t\t\t\t\"b.slug as slug\",\n\t\t\t\t\"b.display_name as display_name\",\n\t\t\t\t\"b.bio as bio\",\n\t\t\t\t\"b.avatar_media_id as avatar_media_id\",\n\t\t\t\t\"m.storage_key as avatar_storage_key\",\n\t\t\t\t\"m.alt as avatar_alt\",\n\t\t\t\t\"b.website_url as website_url\",\n\t\t\t\t\"b.user_id as user_id\",\n\t\t\t\t\"b.is_guest as is_guest\",\n\t\t\t\t\"b.created_at as created_at\",\n\t\t\t\t\"b.updated_at as updated_at\",\n\t\t\t\t\"b.locale as locale\",\n\t\t\t\t\"b.translation_group as translation_group\",\n\t\t\t])\n\t\t\t.where(\"cb.collection_slug\", \"=\", collectionSlug)\n\t\t\t.where(\"cb.content_id\", \"=\", contentId)\n\t\t\t.orderBy(\"cb.sort_order\", \"asc\");\n\t\tif (options?.locale !== undefined) query = query.where(\"b.locale\", \"=\", options.locale);\n\n\t\tconst rows = await query.execute();\n\t\t// Reconstruct byline rows to feed `withCustomFields`. The JOIN selects\n\t\t// the `BylineRow` columns under the `b.` alias plus the avatar media\n\t\t// columns from the `media` LEFT JOIN; carry both through so\n\t\t// `rowToByline` can populate `avatarStorageKey`/`avatarAlt` (otherwise\n\t\t// the join runs but its values are dropped here).\n\t\tconst bylineRows: BylineRowWithAvatar[] = rows.map((row) => ({\n\t\t\tid: row.id,\n\t\t\tslug: row.slug,\n\t\t\tdisplay_name: row.display_name,\n\t\t\tbio: row.bio,\n\t\t\tavatar_media_id: row.avatar_media_id,\n\t\t\tavatar_storage_key: row.avatar_storage_key,\n\t\t\tavatar_alt: row.avatar_alt,\n\t\t\twebsite_url: row.website_url,\n\t\t\tuser_id: row.user_id,\n\t\t\tis_guest: row.is_guest,\n\t\t\tcreated_at: row.created_at,\n\t\t\tupdated_at: row.updated_at,\n\t\t\tlocale: row.locale,\n\t\t\ttranslation_group: row.translation_group,\n\t\t}));\n\t\tconst hydrated = await this.withCustomFields(bylineRows);\n\t\treturn rows.map((row, i) => {\n\t\t\tconst byline = hydrated[i];\n\t\t\tif (!byline) {\n\t\t\t\t// Defensive: hydrated and rows are produced in lock-step;\n\t\t\t\t// this branch is unreachable unless `withCustomFields`\n\t\t\t\t// breaks its contract.\n\t\t\t\tthrow new Error(\"getContentBylines: hydration row count mismatch\");\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tbyline,\n\t\t\t\tsortOrder: row.sort_order,\n\t\t\t\troleLabel: row.role_label,\n\t\t\t};\n\t\t});\n\t}\n\n\t/**\n\t * Does this entry have any explicit byline credits — at any locale?\n\t *\n\t * Used to disambiguate \"no credits exist\" (fall back to author-linked\n\t * byline) from \"credits exist but don't resolve at the requested locale\"\n\t * (strict per-locale model: render no byline). Without this check the\n\t * locale-strict hydration would silently turn a missing translation into\n\t * an author-inferred byline, contradicting editorial intent.\n\t */\n\tasync hasContentBylines(collectionSlug: string, contentId: string): Promise<boolean> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_content_bylines\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"collection_slug\", \"=\", collectionSlug)\n\t\t\t.where(\"content_id\", \"=\", contentId)\n\t\t\t.limit(1)\n\t\t\t.executeTakeFirst();\n\t\treturn row !== undefined;\n\t}\n\n\t/**\n\t * Batch variant of `hasContentBylines`. Returns the set of content IDs\n\t * that have at least one junction row (locale-agnostic).\n\t */\n\tasync hasContentBylinesMany(collectionSlug: string, contentIds: string[]): Promise<Set<string>> {\n\t\tconst result = new Set<string>();\n\t\tif (contentIds.length === 0) return result;\n\n\t\tconst uniqueContentIds = [...new Set(contentIds)];\n\t\tfor (const chunk of chunks(uniqueContentIds, SQL_BATCH_SIZE)) {\n\t\t\tconst rows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_content_bylines\")\n\t\t\t\t.select(\"content_id\")\n\t\t\t\t.distinct()\n\t\t\t\t.where(\"collection_slug\", \"=\", collectionSlug)\n\t\t\t\t.where(\"content_id\", \"in\", chunk)\n\t\t\t\t.execute();\n\t\t\tfor (const row of rows) result.add(row.content_id);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Batch variant of `getContentBylines`. Same strict-per-locale semantics\n\t * applied to the requested locale (single value, not per-entry).\n\t *\n\t * When callers need per-entry-locale filtering (e.g. a list endpoint\n\t * returning entries at mixed locales), they should group the input ids by\n\t * the entry's locale and call this method once per group.\n\t *\n\t * When the caller will issue multiple `getContentBylinesMany` calls in\n\t * one request (e.g. per locale bucket) and wants a *single* batched\n\t * customFields hydration over the union of returned bylines, pass\n\t * `skipHydration: true` on each call and finish with\n\t * `hydrateBylineCustomFields(allBylines)`. The returned bylines carry\n\t * `customFields = {}` until that hydration call runs — matching the\n\t * \"always populated\" invariant from AC #6 — so callers that forget to\n\t * hydrate get an empty map rather than `undefined`.\n\t */\n\tasync getContentBylinesMany(\n\t\tcollectionSlug: string,\n\t\tcontentIds: string[],\n\t\toptions?: { locale?: string; skipHydration?: boolean },\n\t): Promise<Map<string, ContentBylineCredit[]>> {\n\t\tconst result = new Map<string, ContentBylineCredit[]>();\n\t\tif (contentIds.length === 0) return result;\n\n\t\tconst uniqueContentIds = [...new Set(contentIds)];\n\t\tfor (const chunk of chunks(uniqueContentIds, SQL_BATCH_SIZE)) {\n\t\t\tlet query = this.db\n\t\t\t\t.selectFrom(\"_emdash_content_bylines as cb\")\n\t\t\t\t.innerJoin(\"_emdash_bylines as b\", \"b.translation_group\", \"cb.byline_id\")\n\t\t\t\t.leftJoin(\"media as m\", \"m.id\", \"b.avatar_media_id\")\n\t\t\t\t.select([\n\t\t\t\t\t\"cb.content_id as content_id\",\n\t\t\t\t\t\"cb.sort_order as sort_order\",\n\t\t\t\t\t\"cb.role_label as role_label\",\n\t\t\t\t\t\"b.id as id\",\n\t\t\t\t\t\"b.slug as slug\",\n\t\t\t\t\t\"b.display_name as display_name\",\n\t\t\t\t\t\"b.bio as bio\",\n\t\t\t\t\t\"b.avatar_media_id as avatar_media_id\",\n\t\t\t\t\t\"m.storage_key as avatar_storage_key\",\n\t\t\t\t\t\"m.alt as avatar_alt\",\n\t\t\t\t\t\"b.website_url as website_url\",\n\t\t\t\t\t\"b.user_id as user_id\",\n\t\t\t\t\t\"b.is_guest as is_guest\",\n\t\t\t\t\t\"b.created_at as created_at\",\n\t\t\t\t\t\"b.updated_at as updated_at\",\n\t\t\t\t\t\"b.locale as locale\",\n\t\t\t\t\t\"b.translation_group as translation_group\",\n\t\t\t\t])\n\t\t\t\t.where(\"cb.collection_slug\", \"=\", collectionSlug)\n\t\t\t\t.where(\"cb.content_id\", \"in\", chunk)\n\t\t\t\t.orderBy(\"cb.sort_order\", \"asc\");\n\t\t\tif (options?.locale !== undefined) query = query.where(\"b.locale\", \"=\", options.locale);\n\n\t\t\tconst rows = await query.execute();\n\t\t\t// Carry the avatar media columns from the LEFT JOIN through the\n\t\t\t// reshape so `rowToByline` can populate avatarStorageKey/avatarAlt.\n\t\t\tconst bylineRows: BylineRowWithAvatar[] = rows.map((row) => ({\n\t\t\t\tid: row.id,\n\t\t\t\tslug: row.slug,\n\t\t\t\tdisplay_name: row.display_name,\n\t\t\t\tbio: row.bio,\n\t\t\t\tavatar_media_id: row.avatar_media_id,\n\t\t\t\tavatar_storage_key: row.avatar_storage_key,\n\t\t\t\tavatar_alt: row.avatar_alt,\n\t\t\t\twebsite_url: row.website_url,\n\t\t\t\tuser_id: row.user_id,\n\t\t\t\tis_guest: row.is_guest,\n\t\t\t\tcreated_at: row.created_at,\n\t\t\t\tupdated_at: row.updated_at,\n\t\t\t\tlocale: row.locale,\n\t\t\t\ttranslation_group: row.translation_group,\n\t\t\t}));\n\n\t\t\t// When `skipHydration` is set, return BylineSummary objects with\n\t\t\t// `customFields = {}`. The caller is responsible for batching\n\t\t\t// `hydrateBylineCustomFields` across multiple\n\t\t\t// `getContentBylinesMany` calls. Otherwise hydrate per-call —\n\t\t\t// the historical behaviour for solo callers.\n\t\t\tlet bylines: BylineSummary[];\n\t\t\tif (options?.skipHydration === true) {\n\t\t\t\tbylines = bylineRows.map(rowToByline);\n\t\t\t\tfor (const b of bylines) b.customFields = {};\n\t\t\t} else {\n\t\t\t\tbylines = await this.withCustomFields(bylineRows);\n\t\t\t}\n\n\t\t\tfor (let i = 0; i < rows.length; i++) {\n\t\t\t\tconst row = rows[i];\n\t\t\t\tconst byline = bylines[i];\n\t\t\t\tif (!row || !byline) continue;\n\t\t\t\tconst contentId = row.content_id;\n\t\t\t\tconst credit: ContentBylineCredit = {\n\t\t\t\t\tbyline,\n\t\t\t\t\tsortOrder: row.sort_order,\n\t\t\t\t\troleLabel: row.role_label,\n\t\t\t\t};\n\t\t\t\tconst existing = result.get(contentId);\n\t\t\t\tif (existing) {\n\t\t\t\t\texisting.push(credit);\n\t\t\t\t} else {\n\t\t\t\t\tresult.set(contentId, [credit]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Batch-fetch byline profiles linked to user IDs in a single query.\n\t * Strict-locale variant of `findByUserId`.\n\t *\n\t * `skipHydration: true` returns bylines with `customFields = {}` so\n\t * callers issuing multiple `findByUserIds` calls in one request (e.g.\n\t * the per-locale-bucket author-fallback path in `getBylinesForEntries`)\n\t * can defer customFields hydration to a single batched\n\t * `hydrateBylineCustomFields` call across the union — keeping the\n\t * Phase 3 query-count envelope at \"+1 group-shared query per\n\t * hydration pass\" even when buckets fetch disjoint author bylines.\n\t */\n\tasync findByUserIds(\n\t\tuserIds: string[],\n\t\toptions?: { locale?: string; skipHydration?: boolean },\n\t): Promise<Map<string, BylineSummary>> {\n\t\tconst result = new Map<string, BylineSummary>();\n\t\tif (userIds.length === 0) return result;\n\n\t\tfor (const chunk of chunks(userIds, SQL_BATCH_SIZE)) {\n\t\t\t// LEFT JOIN media so author-inferred bylines (the fallback path in\n\t\t\t// `getBylinesForEntries`) carry the same render-ready avatar storage\n\t\t\t// key as explicitly-credited bylines do.\n\t\t\tlet query = this.db\n\t\t\t\t.selectFrom(\"_emdash_bylines as b\")\n\t\t\t\t.leftJoin(\"media as m\", \"m.id\", \"b.avatar_media_id\")\n\t\t\t\t.select([\n\t\t\t\t\t\"b.id as id\",\n\t\t\t\t\t\"b.slug as slug\",\n\t\t\t\t\t\"b.display_name as display_name\",\n\t\t\t\t\t\"b.bio as bio\",\n\t\t\t\t\t\"b.avatar_media_id as avatar_media_id\",\n\t\t\t\t\t\"m.storage_key as avatar_storage_key\",\n\t\t\t\t\t\"m.alt as avatar_alt\",\n\t\t\t\t\t\"b.website_url as website_url\",\n\t\t\t\t\t\"b.user_id as user_id\",\n\t\t\t\t\t\"b.is_guest as is_guest\",\n\t\t\t\t\t\"b.created_at as created_at\",\n\t\t\t\t\t\"b.updated_at as updated_at\",\n\t\t\t\t\t\"b.locale as locale\",\n\t\t\t\t\t\"b.translation_group as translation_group\",\n\t\t\t\t])\n\t\t\t\t.where(\"b.user_id\", \"in\", chunk);\n\t\t\tif (options?.locale !== undefined) query = query.where(\"b.locale\", \"=\", options.locale);\n\n\t\t\tconst rows = await query.execute();\n\t\t\tlet bylines: BylineSummary[];\n\t\t\tif (options?.skipHydration === true) {\n\t\t\t\tbylines = rows.map(rowToByline);\n\t\t\t\tfor (const b of bylines) b.customFields = {};\n\t\t\t} else {\n\t\t\t\tbylines = await this.withCustomFields(rows);\n\t\t\t}\n\n\t\t\tfor (let i = 0; i < rows.length; i++) {\n\t\t\t\tconst row = rows[i];\n\t\t\t\tconst summary = bylines[i];\n\t\t\t\tif (!row || !summary || !row.user_id) continue;\n\t\t\t\tresult.set(row.user_id, summary);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Clone every junction row from `sourceContentId` to `targetContentId`,\n\t * preserving `sort_order` and `role_label`. Used by the content\n\t * translation flow: a newly created translation inherits the source's\n\t * byline credits at the storage level. Because the junction stores\n\t * `translation_group` (not a row id), the copy is locale-agnostic — the\n\t * credits resolve to whichever locale variants of each byline exist when\n\t * the translated entry is hydrated.\n\t *\n\t * No-op when the source has no credits. Skips when the target already\n\t * has credits (idempotent for re-runs).\n\t */\n\tasync copyContentBylines(\n\t\tcollection: string,\n\t\tsourceContentId: string,\n\t\ttargetContentId: string,\n\t): Promise<void> {\n\t\tvalidateIdentifier(collection, \"collection slug\");\n\t\tconst tableName = `ec_${collection}`;\n\t\tvalidateIdentifier(tableName, \"content table\");\n\n\t\t// Like `setContentBylines`, this method is expected to be called\n\t\t// within a transaction context (content handlers wrap in\n\t\t// withTransaction). All operations use `this.db` directly so an\n\t\t// outer transaction can serialise the copy alongside the create.\n\t\tconst existing = await this.db\n\t\t\t.selectFrom(\"_emdash_content_bylines\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"collection_slug\", \"=\", collection)\n\t\t\t.where(\"content_id\", \"=\", targetContentId)\n\t\t\t.executeTakeFirst();\n\t\tif (existing) return;\n\n\t\tconst sourceRows = await this.db\n\t\t\t.selectFrom(\"_emdash_content_bylines\")\n\t\t\t.select([\"byline_id\", \"sort_order\", \"role_label\"])\n\t\t\t.where(\"collection_slug\", \"=\", collection)\n\t\t\t.where(\"content_id\", \"=\", sourceContentId)\n\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t.execute();\n\t\tif (sourceRows.length === 0) return;\n\n\t\tconst now = new Date().toISOString();\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_content_bylines\")\n\t\t\t.values(\n\t\t\t\tsourceRows.map((row) => ({\n\t\t\t\t\tid: ulid(),\n\t\t\t\t\tcollection_slug: collection,\n\t\t\t\t\tcontent_id: targetContentId,\n\t\t\t\t\tbyline_id: row.byline_id,\n\t\t\t\t\tsort_order: row.sort_order,\n\t\t\t\t\trole_label: row.role_label,\n\t\t\t\t\tcreated_at: now,\n\t\t\t\t})),\n\t\t\t)\n\t\t\t.execute();\n\n\t\t// Mirror primary_byline_id from source so the cached pointer on the\n\t\t// target row matches the junction state we just wrote.\n\t\tconst firstByline = sourceRows[0]?.byline_id ?? null;\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET primary_byline_id = ${firstByline}\n\t\t\tWHERE id = ${targetContentId}\n\t\t`.execute(this.db);\n\t}\n\n\t/**\n\t * Replace the set of byline credits on a content entry. Accepts row ids\n\t * at the wire (consistent with how the admin sends them), translates\n\t * each to its `translation_group` on write, and stores the group in\n\t * `_emdash_content_bylines.byline_id` and `ec_*.primary_byline_id`.\n\t *\n\t * The returned credits are hydrated with strict-locale matching at the\n\t * locale of the rows the caller supplied (i.e. the locale of the byline\n\t * each `bylineId` resolves to) — adequate for the autosave round-trip,\n\t * which then re-hydrates the entry against its own locale separately.\n\t */\n\tasync setContentBylines(\n\t\tcollectionSlug: string,\n\t\tcontentId: string,\n\t\tinputBylines: ContentBylineInput[],\n\t): Promise<ContentBylineCredit[]> {\n\t\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\t\tconst tableName = `ec_${collectionSlug}`;\n\t\tvalidateIdentifier(tableName, \"content table\");\n\n\t\t// Resolve each wire row id to its translation_group up front so we\n\t\t// can (a) validate the rows exist and (b) dedupe by the value that\n\t\t// actually lands in the junction. Deduping by wire row id BEFORE\n\t\t// resolving would let two locale siblings of the same byline slip\n\t\t// through and trigger a UNIQUE(collection, content, byline_id)\n\t\t// failure at insert time. A single SELECT keeps this O(1) DB\n\t\t// calls regardless of how many credits are being set.\n\t\tconst idToGroup = new Map<string, string>();\n\t\tif (inputBylines.length > 0) {\n\t\t\tconst wireIds = [...new Set(inputBylines.map((item) => item.bylineId))];\n\t\t\tconst rows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t\t.select([\"id\", \"translation_group\"])\n\t\t\t\t.where(\"id\", \"in\", wireIds)\n\t\t\t\t.execute();\n\t\t\tif (rows.length !== wireIds.length) {\n\t\t\t\tthrow new Error(\"One or more byline IDs do not exist\");\n\t\t\t}\n\t\t\tfor (const row of rows) {\n\t\t\t\tidToGroup.set(row.id, row.translation_group ?? row.id);\n\t\t\t}\n\t\t}\n\n\t\t// Dedupe by translation_group. Preserves the order of first\n\t\t// occurrence so the editor's intent (which sibling appears first)\n\t\t// is honored. `roleLabel` follows the first occurrence too.\n\t\tconst seenGroups = new Set<string>();\n\t\tconst bylines: Array<ContentBylineInput & { group: string }> = [];\n\t\tfor (const item of inputBylines) {\n\t\t\tconst group = idToGroup.get(item.bylineId);\n\t\t\tif (!group) {\n\t\t\t\tthrow new Error(`Missing translation_group for byline ${item.bylineId}`);\n\t\t\t}\n\t\t\tif (seenGroups.has(group)) continue;\n\t\t\tseenGroups.add(group);\n\t\t\tbylines.push({ ...item, group });\n\t\t}\n\n\t\t// This method is expected to be called within a transaction context\n\t\t// (content handlers wrap in withTransaction, seed applies sequentially).\n\t\t// All operations use this.db directly -- callers are responsible for\n\t\t// wrapping in a transaction when atomicity is required.\n\t\tawait this.db\n\t\t\t.deleteFrom(\"_emdash_content_bylines\")\n\t\t\t.where(\"collection_slug\", \"=\", collectionSlug)\n\t\t\t.where(\"content_id\", \"=\", contentId)\n\t\t\t.execute();\n\n\t\tfor (let i = 0; i < bylines.length; i++) {\n\t\t\tconst item = bylines[i];\n\t\t\tif (!item) continue;\n\t\t\tawait this.db\n\t\t\t\t.insertInto(\"_emdash_content_bylines\")\n\t\t\t\t.values({\n\t\t\t\t\tid: ulid(),\n\t\t\t\t\tcollection_slug: collectionSlug,\n\t\t\t\t\tcontent_id: contentId,\n\t\t\t\t\tbyline_id: item.group,\n\t\t\t\t\tsort_order: i,\n\t\t\t\t\trole_label: item.roleLabel ?? null,\n\t\t\t\t\tcreated_at: new Date().toISOString(),\n\t\t\t\t})\n\t\t\t\t.execute();\n\t\t}\n\n\t\tconst primaryGroup = bylines[0]?.group ?? null;\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET primary_byline_id = ${primaryGroup}\n\t\t\tWHERE id = ${contentId}\n\t\t`.execute(this.db);\n\n\t\treturn await this.getContentBylines(collectionSlug, contentId);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"byline-CAhk4FrG.mjs","names":[],"sources":["../src/bylines/field-defs-cache.ts","../src/database/repositories/byline.ts"],"sourcesContent":["/**\n * Byline field-definitions cache\n *\n * Discussion #1174 / Phase 3. Two-tier cache for the byline custom-field\n * registry, mirroring the `settings/index.ts` pattern.\n *\n * **Tier 1 — per-isolate (globalThis).** Field definitions change rarely\n * but are read on every byline hydration (admin pages, content rendering,\n * API responses). Caching at the isolate level drops the SELECT-from-\n * `_emdash_byline_fields` from once-per-hydration to once-per-isolate-\n * after-bump. The cache holds a Promise (not the resolved value) so\n * concurrent cold-isolate readers share the in-flight query.\n *\n * Stored on globalThis under `Symbol.for(\"emdash:byline-field-defs\")` so\n * Vite SSR chunk duplication can't produce two independent caches (same\n * pattern as `request-cache.ts` and `request-context.ts`).\n *\n * **Tier 2 — per-request.** Wraps both the version read and the defs\n * fetch in `requestCached` so a single page render that hits byline\n * hydration multiple times (e.g. list view + individual byline lookups\n * in a sidebar) pays at most one version read and one defs fetch in\n * total. The defs cache key includes the version, so a (highly\n * unlikely) mid-request bump still produces a self-consistent view —\n * the second call sees a different key and refetches.\n *\n * **Invalidation.** `options.byline_fields_version` is bumped by every\n * `BylineSchemaRegistry` mutation (Phase 2). Each isolate independently\n * reads the persisted version on the next request and compares against\n * its cached version; mismatch triggers a refetch and overwrite. Other\n * isolates see the change within one request after the bump propagates.\n *\n * **Isolated databases bypass the global cache.** Playground and DO\n * preview sessions set `requestContext.dbIsIsolated = true`, signalling\n * the per-request `db` points at an isolated schema that may diverge\n * from the singleton. Schema-derived caches keyed by the singleton's\n * version would silently leak the singleton's defs into the isolated\n * request. We follow the `loader.ts:74` `getTaxonomyNames` precedent:\n * skip both reading from and writing to the global holder when the\n * request is isolated. The per-request cache (`requestCached`) is keyed\n * by the WeakMap'd `EmDashRequestContext`, so it can't cross-pollinate\n * between requests — it stays in play even for isolated DBs.\n *\n * **Why a versioned cache and not a TTL?** The version counter gives\n * deterministic invalidation without the staleness window a TTL would\n * impose. Field-definition changes need to be visible to the next\n * request, not eventually. The cost is one cheap `options` read per\n * request — cheaper than the field-defs fetch it replaces, and cheaper\n * than maintaining a TTL state machine.\n */\n\nimport type { Kysely } from \"kysely\";\n\nimport type { Database } from \"../database/types.js\";\nimport { requestCached } from \"../request-cache.js\";\nimport { getRequestContext } from \"../request-context.js\";\nimport { BylineSchemaRegistry } from \"../schema/byline-registry.js\";\nimport type { BylineFieldDefinition } from \"../schema/types.js\";\n\ninterface FieldDefsHolder {\n\t/** In-flight or resolved defs promise for the cached version. Null until first read. */\n\tcached: Promise<BylineFieldDefinition[]> | null;\n\t/** Persisted-version value that `cached` was fetched against. */\n\tcachedVersion: number;\n}\n\nconst HOLDER_KEY = Symbol.for(\"emdash:byline-field-defs\");\nconst g = globalThis as Record<symbol, unknown>;\nconst holder: FieldDefsHolder =\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- globalThis singleton pattern (see request-cache.ts)\n\t(g[HOLDER_KEY] as FieldDefsHolder | undefined) ??\n\t(() => {\n\t\tconst h: FieldDefsHolder = { cached: null, cachedVersion: -1 };\n\t\tg[HOLDER_KEY] = h;\n\t\treturn h;\n\t})();\n\nconst REQUEST_CACHE_KEY_VERSION = \"byline-fields-version\";\nconst REQUEST_CACHE_KEY_DEFS_PREFIX = \"byline-field-defs:\";\n\n/**\n * Read the persisted `options.byline_fields_version` counter. Cached for\n * the duration of the current request via `requestCached`. Returns `0`\n * when the row is missing (matches `BylineSchemaRegistry.getVersion`).\n */\nasync function getBylineFieldsVersion(db: Kysely<Database>): Promise<number> {\n\treturn requestCached(REQUEST_CACHE_KEY_VERSION, () => new BylineSchemaRegistry(db).getVersion());\n}\n\n/**\n * Resolve registered byline custom-field definitions. Two-tier cache:\n * per-request via `requestCached`, then per-isolate via the global\n * holder.\n *\n * The global holder is bypassed for isolated requests (playground / DO\n * preview, which point at a divergent schema) and for dirty versions\n * (odd counter — see `BylineSchemaRegistry`'s class JSDoc — indicates\n * an in-flight or crashed mutation). Both bypass paths still hit the\n * per-request cache, so a single render dedupes within itself.\n *\n * Always returns an array. Empty = no custom fields registered.\n */\nexport async function getBylineFieldDefs(db: Kysely<Database>): Promise<BylineFieldDefinition[]> {\n\tconst isolated = getRequestContext()?.dbIsIsolated === true;\n\tconst version = await getBylineFieldsVersion(db);\n\tconst dirty = version % 2 !== 0;\n\treturn requestCached(`${REQUEST_CACHE_KEY_DEFS_PREFIX}${version}`, async () => {\n\t\tif (isolated || dirty) {\n\t\t\treturn new BylineSchemaRegistry(db).listFields();\n\t\t}\n\t\tif (holder.cached !== null && holder.cachedVersion === version) {\n\t\t\treturn holder.cached;\n\t\t}\n\t\tconst defs = new BylineSchemaRegistry(db).listFields().catch((error) => {\n\t\t\tif (holder.cached === defs) {\n\t\t\t\tholder.cached = null;\n\t\t\t\tholder.cachedVersion = -1;\n\t\t\t}\n\t\t\tthrow error;\n\t\t});\n\t\tholder.cached = defs;\n\t\tholder.cachedVersion = version;\n\t\treturn defs;\n\t});\n}\n\n/**\n * Test/internal helper: clear the per-isolate cache. Useful for unit\n * tests that mutate the registry directly and need to force a refetch\n * without going through the full version-bump path.\n *\n * Production code paths should rely on the version counter for\n * invalidation — calling this from a write path would bypass the\n * coordination that lets other isolates see the change.\n */\nexport function resetBylineFieldDefsCacheForTests(): void {\n\tholder.cached = null;\n\tholder.cachedVersion = -1;\n}\n","import { sql, type Kysely, type Selectable } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { getBylineFieldDefs } from \"../../bylines/field-defs-cache.js\";\nimport {\n\tclearRequestCacheEntry,\n\tpeekRequestCache,\n\tsetRequestCacheEntry,\n} from \"../../request-cache.js\";\nimport type { BylineFieldDefinition, CustomFieldValue } from \"../../schema/types.js\";\nimport { chunks, SQL_BATCH_SIZE } from \"../../utils/chunks.js\";\nimport { listTablesLike } from \"../dialect-helpers.js\";\nimport { withTransaction } from \"../transaction.js\";\nimport type { BylineTable, Database } from \"../types.js\";\nimport { validateIdentifier } from \"../validate.js\";\nimport {\n\tdecodeCursor,\n\tEmDashValidationError,\n\tencodeCursor,\n\ttype BylineSummary,\n\ttype ContentBylineCredit,\n\ttype FindManyResult,\n} from \"./types.js\";\n\ntype BylineRow = Selectable<BylineTable>;\n\n/**\n * A byline row optionally augmented with the avatar's media columns, folded in\n * by the `LEFT JOIN media` in the content-credit hydration queries. The plain\n * `selectAll()` finders produce rows without these keys, so they're optional\n * and `rowToByline` defaults them to null.\n */\ntype BylineRowWithAvatar = BylineRow & {\n\tavatar_storage_key?: string | null;\n\tavatar_alt?: string | null;\n};\n\nexport interface CreateBylineInput {\n\tslug: string;\n\tdisplayName: string;\n\tbio?: string | null;\n\tavatarMediaId?: string | null;\n\twebsiteUrl?: string | null;\n\tuserId?: string | null;\n\tisGuest?: boolean;\n\t/**\n\t * Locale this byline row belongs to. When omitted, the DB DEFAULT (the\n\t * configured `defaultLocale` after migration 040) is used. Keeps behaviour\n\t * consistent with `TaxonomyRepository.create`.\n\t */\n\tlocale?: string;\n\t/**\n\t * When set, the new row joins the source byline's translation_group rather\n\t * than minting a fresh one. The source must exist; otherwise the create\n\t * throws. Mirrors `TaxonomyRepository.create`.\n\t */\n\ttranslationOf?: string;\n\t/**\n\t * Byline custom-field values to seed on the new row (Phase 6 of\n\t * Discussion #1174). Same semantics as `UpdateBylineInput.customFields`:\n\t * keys must match registered slugs in `_emdash_byline_fields`, values\n\t * are validated against the field's type, and writes route to\n\t * `_emdash_byline_field_values` (translatable) or\n\t * `_emdash_byline_field_group_values` (group-shared). Validation runs\n\t * before the row insert so a bad value can't leave a bare byline behind.\n\t */\n\tcustomFields?: Record<string, unknown>;\n}\n\nexport interface UpdateBylineInput {\n\tslug?: string;\n\tdisplayName?: string;\n\tbio?: string | null;\n\tavatarMediaId?: string | null;\n\twebsiteUrl?: string | null;\n\tuserId?: string | null;\n\tisGuest?: boolean;\n\t/**\n\t * Byline custom-field values to write (Phase 3 of Discussion #1174).\n\t *\n\t * Each key must match a registered slug in `_emdash_byline_fields`;\n\t * unknown keys throw `EmDashValidationError`. Per-field writes route\n\t * to `_emdash_byline_field_values` (when the field's `translatable`\n\t * flag is true) or `_emdash_byline_field_group_values` (when false).\n\t * A value of `null` clears the row.\n\t *\n\t * Values are validated against the field's type:\n\t * - `string` / `text` / `url` accept a `string`\n\t * - `boolean` accepts a `boolean`\n\t * - `select` accepts a `string` that appears in `validation.options`\n\t *\n\t * Writes are idempotent (`INSERT … ON CONFLICT DO UPDATE`), so\n\t * retrying the same update produces the same DB state.\n\t */\n\tcustomFields?: Record<string, unknown>;\n}\n\nexport interface ContentBylineInput {\n\tbylineId: string;\n\troleLabel?: string | null;\n}\n\nfunction rowToByline(row: BylineRowWithAvatar): BylineSummary {\n\treturn {\n\t\tid: row.id,\n\t\tslug: row.slug,\n\t\tdisplayName: row.display_name,\n\t\tbio: row.bio,\n\t\tavatarMediaId: row.avatar_media_id,\n\t\tavatarStorageKey: row.avatar_storage_key ?? null,\n\t\tavatarAlt: row.avatar_alt ?? null,\n\t\twebsiteUrl: row.website_url,\n\t\tuserId: row.user_id,\n\t\tisGuest: row.is_guest === 1,\n\t\tcreatedAt: row.created_at,\n\t\tupdatedAt: row.updated_at,\n\t\tlocale: row.locale,\n\t\ttranslationGroup: row.translation_group,\n\t};\n}\n\n/**\n * Merge a single decoded value into a `BylineSummary.customFields` map.\n * Centralised so the merge semantics (null storage, JSON.parse failure\n * handling) live in one place across both translatable and group-shared\n * paths.\n *\n * A stored row with `value = NULL` (representing an explicit null) is\n * surfaced as `null` in `customFields`. A row with a malformed JSON\n * payload is dropped silently with a `console.warn` — a corrupted\n * payload shouldn't break the entire byline hydration; the field-defs\n * cache will let admins replace the value, and the warning makes the\n * issue debuggable. (Storage path uses `JSON.stringify`, so the only\n * way to get malformed JSON is direct DB tampering or a future\n * migration bug.)\n */\nfunction assignCustomFieldValue(\n\tsummary: BylineSummary,\n\tfield: BylineFieldDefinition,\n\tstored: string | null,\n): void {\n\tconst target = summary.customFields ?? {};\n\tif (stored === null) {\n\t\ttarget[field.slug] = null;\n\t} else {\n\t\ttry {\n\t\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- coerceFieldValue ran at write time, see field-defs-cache.ts\n\t\t\ttarget[field.slug] = JSON.parse(stored) as CustomFieldValue;\n\t\t} catch {\n\t\t\tconsole.warn(\n\t\t\t\t`[BylineRepository] dropping malformed JSON for byline=${summary.id} ` +\n\t\t\t\t\t`field=${field.slug}: ${stored.slice(0, 60)}`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t}\n\tsummary.customFields = target;\n}\n\n/**\n * Coerce a raw write-path value to `CustomFieldValue`, throwing\n * `EmDashValidationError` on type mismatch. `null` clears the field\n * (DELETE in the write path).\n *\n * TODO: `field.required` is not enforced. The admin UI exposes the\n * toggle but the backend accepts missing values; design pass needed\n * on the enforcement model.\n */\nfunction coerceFieldValue(field: BylineFieldDefinition, raw: unknown): CustomFieldValue {\n\tif (raw === null) return null;\n\n\tswitch (field.type) {\n\t\tcase \"string\":\n\t\tcase \"text\": {\n\t\t\tif (typeof raw !== \"string\") {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Byline field \"${field.slug}\" expects a string value (received ${typeof raw})`,\n\t\t\t\t\t{ slug: field.slug, type: field.type, received: typeof raw },\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn raw;\n\t\t}\n\t\tcase \"url\": {\n\t\t\tif (typeof raw !== \"string\") {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Byline field \"${field.slug}\" expects a string value (received ${typeof raw})`,\n\t\t\t\t\t{ slug: field.slug, type: field.type, received: typeof raw },\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Empty string round-trips as a clear from the admin UI; any\n\t\t\t// non-empty value must be a valid http(s) URL. The scheme\n\t\t\t// allowlist mirrors `httpUrl` in `api/schemas/common.ts` —\n\t\t\t// `new URL` alone would accept `javascript:`/`data:` etc.\n\t\t\tif (raw === \"\") return raw;\n\t\t\tlet parsed: URL;\n\t\t\ttry {\n\t\t\t\tparsed = new URL(raw);\n\t\t\t} catch {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Byline field \"${field.slug}\" expects a valid URL (received \"${raw}\")`,\n\t\t\t\t\t{ slug: field.slug, type: field.type, received: raw },\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Byline field \"${field.slug}\" must use http or https scheme (received \"${parsed.protocol}\")`,\n\t\t\t\t\t{ slug: field.slug, type: field.type, received: raw, protocol: parsed.protocol },\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn raw;\n\t\t}\n\t\tcase \"boolean\": {\n\t\t\tif (typeof raw !== \"boolean\") {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Byline field \"${field.slug}\" expects a boolean value (received ${typeof raw})`,\n\t\t\t\t\t{ slug: field.slug, type: field.type, received: typeof raw },\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn raw;\n\t\t}\n\t\tcase \"select\": {\n\t\t\tif (typeof raw !== \"string\") {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Byline field \"${field.slug}\" expects a string value (received ${typeof raw})`,\n\t\t\t\t\t{ slug: field.slug, type: field.type, received: typeof raw },\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst options = field.validation?.options ?? [];\n\t\t\tif (!options.includes(raw)) {\n\t\t\t\tthrow new EmDashValidationError(\n\t\t\t\t\t`Byline field \"${field.slug}\" value \"${raw}\" is not one of the registered choices`,\n\t\t\t\t\t{ slug: field.slug, value: raw, options },\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn raw;\n\t\t}\n\t}\n}\n\n/**\n * Byline repository for content credits.\n *\n * Bylines are per-locale (migration 040). Translations of the same byline\n * share a `translation_group` ULID. `_emdash_content_bylines.byline_id` and\n * `ec_*.primary_byline_id` store the translation_group (not a row id) so a\n * single credit spans every locale variant of a byline.\n *\n * The repository does not resolve locale fallbacks on its own — callers\n * supply the locale they want. Hydration is strict per locale: a credit at\n * locale X renders iff a byline row exists at locale X within the credited\n * translation group. This mirrors `TaxonomyRepository.getTermsForEntry` and\n * the convention established by PR #916.\n *\n * Runtime helpers in `packages/core/src/bylines/index.ts` may layer fallback\n * resolution on top for the \"look up one byline by slug\" path, but the\n * relation-hydration methods on this class are always strict.\n */\nexport class BylineRepository {\n\tconstructor(private db: Kysely<Database>) {}\n\n\t// ============================================\n\t// Custom-field hydration (Phase 3 of #1174)\n\t// ============================================\n\n\t/**\n\t * Merge `customFields` onto each `BylineSummary` produced from the\n\t * given rows. Two batched queries total — one against\n\t * `_emdash_byline_field_values` (keyed by `byline_id`), one against\n\t * `_emdash_byline_field_group_values` (keyed by `translation_group`)\n\t * — both chunked at `SQL_BATCH_SIZE` for D1's bound-parameter cap.\n\t *\n\t * When zero fields are registered, every row gets `customFields = {}`\n\t * with no value-table reads (the field-defs cache returns `[]`).\n\t * Group-shared values are looked up via the row's `translation_group`,\n\t * so every locale sibling of the same byline identity sees the same\n\t * non-translatable value without re-reading per row.\n\t *\n\t * **Duplicate-row handling.** Callers (notably `getContentBylinesMany`\n\t * for list views with repeated authors) can pass the same byline row\n\t * multiple times. We assign values by *iterating both `rows` and\n\t * `summaries` in lockstep by index*, not by deduping into a Map keyed\n\t * on byline id. A Map approach silently drops earlier duplicates' merge\n\t * step (last writer wins, earlier instances keep their initial `{}`).\n\t * Iterating by index gives every duplicate its own merged copy.\n\t *\n\t * Hydration is *strict per row* — values are merged onto whichever\n\t * `BylineRow` produced them. Fallback semantics (e.g. \"if no value\n\t * for this locale, show the default-locale value\") are not the\n\t * repository's concern; consumers layer them on top if wanted, the\n\t * same way `BylineRepository` doesn't resolve locale fallback for\n\t * the base byline lookup.\n\t */\n\tprivate async withCustomFields(rows: BylineRow[]): Promise<BylineSummary[]> {\n\t\tconst summaries = rows.map(rowToByline);\n\t\t// Always populate `customFields = {}` (PR plan AC #6) — even when\n\t\t// no fields are registered, every BylineSummary carries the empty\n\t\t// object. A fresh object per summary so duplicate rows don't share\n\t\t// state.\n\t\tfor (const summary of summaries) {\n\t\t\tsummary.customFields = {};\n\t\t}\n\t\tawait this.applyCustomFieldsTo(summaries);\n\t\treturn summaries;\n\t}\n\n\tprivate async withCustomFieldsOne(row: BylineRow | undefined): Promise<BylineSummary | null> {\n\t\tif (!row) return null;\n\t\tconst [result] = await this.withCustomFields([row]);\n\t\treturn result ?? null;\n\t}\n\n\t/**\n\t * Hydrate `customFields` on each `BylineSummary`, mutating in place.\n\t *\n\t * The public entry point for callers that fetch byline rows in\n\t * multiple passes (e.g. `getBylinesForEntries`, which buckets by\n\t * locale and calls `getContentBylinesMany` per bucket) and want a\n\t * single batched hydration over the union of bylines, not one per\n\t * pass. Use with the `skipHydration` option on the read methods to\n\t * defer customFields work to a single call here.\n\t *\n\t * Two batched queries total (translatable + group-shared) regardless\n\t * of how many bylines, locales, or translation_groups are in the\n\t * input — meets the Phase 3 query-count envelope for mixed-locale\n\t * list views even when sibling locales reference disjoint\n\t * translation_groups.\n\t *\n\t * Replaces any existing `customFields` on each summary with a freshly\n\t * fetched map. Callers that want to merge rather than replace should\n\t * not use this entry point.\n\t */\n\tasync hydrateBylineCustomFields(summaries: BylineSummary[]): Promise<void> {\n\t\tfor (const summary of summaries) {\n\t\t\tsummary.customFields = {};\n\t\t}\n\t\tawait this.applyCustomFieldsTo(summaries);\n\t}\n\n\t/**\n\t * Shared merge engine for `withCustomFields` and\n\t * `hydrateBylineCustomFields`. Reads field defs (cached), batches the\n\t * translatable + group-shared fetches, and walks `summaries` directly\n\t * to apply values.\n\t *\n\t * Iterates `summaries` (not a `summaryById` map) so duplicate\n\t * `BylineSummary` objects sharing the same `id` — e.g. the same\n\t * author credited to multiple entries — each get their own merged\n\t * values. The previous Map-based dedup silently dropped earlier\n\t * duplicates' merge step.\n\t */\n\tprivate async applyCustomFieldsTo(summaries: BylineSummary[]): Promise<void> {\n\t\tif (summaries.length === 0) return;\n\n\t\tconst defs = await getBylineFieldDefs(this.db);\n\t\tif (defs.length === 0) return;\n\n\t\tconst fieldById = new Map(defs.map((d) => [d.id, d]));\n\n\t\t// Translatable values, batched by byline_id (unique per locale, so\n\t\t// IDs across different locale buckets don't collide — one batched\n\t\t// query covers everything).\n\t\tconst translatableByByline = new Map<string, Map<string, string | null>>();\n\t\tconst bylineIds = [...new Set(summaries.map((s) => s.id))];\n\t\tfor (const chunk of chunks(bylineIds, SQL_BATCH_SIZE)) {\n\t\t\tconst trRows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_byline_field_values\")\n\t\t\t\t.select([\"byline_id\", \"field_id\", \"value\"])\n\t\t\t\t.where(\"byline_id\", \"in\", chunk)\n\t\t\t\t.execute();\n\t\t\tfor (const trRow of trRows) {\n\t\t\t\tlet fieldMap = translatableByByline.get(trRow.byline_id);\n\t\t\t\tif (!fieldMap) {\n\t\t\t\t\tfieldMap = new Map();\n\t\t\t\t\ttranslatableByByline.set(trRow.byline_id, fieldMap);\n\t\t\t\t}\n\t\t\t\tfieldMap.set(trRow.field_id, trRow.value);\n\t\t\t}\n\t\t}\n\n\t\t// Group-shared values, batched over the union of translation_groups,\n\t\t// with per-group request-cache priming so subsequent calls within\n\t\t// the same request share the lookup. Together with the\n\t\t// `hydrateBylineCustomFields` + `skipHydration` flow in\n\t\t// `getBylinesForEntries`, this keeps mixed-locale list views to\n\t\t// **one** group-shared query per request, even for disjoint\n\t\t// translation_groups across locale buckets.\n\t\tconst groups = [\n\t\t\t...new Set(\n\t\t\t\tsummaries\n\t\t\t\t\t.map((s) => s.translationGroup)\n\t\t\t\t\t.filter((g): g is string => typeof g === \"string\" && g.length > 0),\n\t\t\t),\n\t\t];\n\t\tconst groupByGroup = await this.loadGroupValuesByIds(groups);\n\n\t\t// Each loop gates on `field.translatable` so a row in the wrong\n\t\t// owner table (e.g. left over from a translatable flip) can't\n\t\t// leak into hydration.\n\t\tfor (const summary of summaries) {\n\t\t\tconst trValues = translatableByByline.get(summary.id);\n\t\t\tif (trValues) {\n\t\t\t\tfor (const [fieldId, value] of trValues) {\n\t\t\t\t\tconst field = fieldById.get(fieldId);\n\t\t\t\t\tif (!field || !field.translatable) continue;\n\t\t\t\t\tassignCustomFieldValue(summary, field, value);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (summary.translationGroup) {\n\t\t\t\tconst grpValues = groupByGroup.get(summary.translationGroup);\n\t\t\t\tif (grpValues) {\n\t\t\t\t\tfor (const [fieldId, value] of grpValues) {\n\t\t\t\t\t\tconst field = fieldById.get(fieldId);\n\t\t\t\t\t\tif (!field || field.translatable) continue;\n\t\t\t\t\t\tassignCustomFieldValue(summary, field, value);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the group-shared custom-field values for a set of\n\t * translation_groups, sharing work across hydration calls within the\n\t * same request via per-group `requestCached` entries.\n\t *\n\t * The non-translatable storage table (`_emdash_byline_field_group_values`)\n\t * is keyed by `translation_group`, which is locale-agnostic. Combining\n\t * this method with `skipHydration` on `getContentBylinesMany` and a\n\t * single `hydrateBylineCustomFields` call (see\n\t * `getBylinesForEntries`) keeps mixed-locale list hydration to **one**\n\t * batched group-shared SQL per request — even with disjoint\n\t * translation_groups across locale buckets. Solo callers (`findById`,\n\t * `findMany`, etc.) still get the same per-call batching they had\n\t * before; the cache simply means a second call in the same request\n\t * for an overlapping group is free.\n\t *\n\t * Cache key: `byline-field-group-values:${groupId}` — one entry per\n\t * group. Writes use `setRequestCacheEntry` (idempotent, doesn't\n\t * overwrite); `BylineRepository.update` calls `clearRequestCacheEntry`\n\t * after a group-shared write to keep the cache fresh within the same\n\t * request.\n\t */\n\tprivate async loadGroupValuesByIds(\n\t\tgroups: string[],\n\t): Promise<Map<string, Map<string, string | null>>> {\n\t\tconst result = new Map<string, Map<string, string | null>>();\n\t\tif (groups.length === 0) return result;\n\n\t\t// First pass: pull any already-cached groups from the request scope.\n\t\tconst missing: string[] = [];\n\t\tfor (const g of groups) {\n\t\t\tconst cached = peekRequestCache<Map<string, string | null>>(`byline-field-group-values:${g}`);\n\t\t\tif (cached) {\n\t\t\t\tresult.set(g, await cached);\n\t\t\t} else {\n\t\t\t\tmissing.push(g);\n\t\t\t}\n\t\t}\n\n\t\tif (missing.length === 0) return result;\n\n\t\t// Second pass: one batched SQL for the union of all missing groups\n\t\t// (chunked for D1's bound-parameter cap). Initialise empty maps for\n\t\t// missing groups so the primed cache covers \"this group has no\n\t\t// values\" — preventing a re-fetch on subsequent calls.\n\t\tconst fetched = new Map<string, Map<string, string | null>>();\n\t\tfor (const g of missing) fetched.set(g, new Map());\n\t\tfor (const chunk of chunks(missing, SQL_BATCH_SIZE)) {\n\t\t\tconst grpRows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_byline_field_group_values\")\n\t\t\t\t.select([\"translation_group\", \"field_id\", \"value\"])\n\t\t\t\t.where(\"translation_group\", \"in\", chunk)\n\t\t\t\t.execute();\n\t\t\tfor (const grpRow of grpRows) {\n\t\t\t\tconst fieldMap = fetched.get(grpRow.translation_group);\n\t\t\t\tif (!fieldMap) continue;\n\t\t\t\tfieldMap.set(grpRow.field_id, grpRow.value);\n\t\t\t}\n\t\t}\n\n\t\tfor (const g of missing) {\n\t\t\tconst m = fetched.get(g);\n\t\t\tif (!m) continue;\n\t\t\tsetRequestCacheEntry(`byline-field-group-values:${g}`, m);\n\t\t\tresult.set(g, m);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t// ============================================\n\t// Reads\n\t// ============================================\n\n\tasync findById(id: string): Promise<BylineSummary | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\t\treturn this.withCustomFieldsOne(row);\n\t}\n\n\t/**\n\t * Find a byline by slug. When `locale` is provided, filter by it strictly.\n\t * When omitted, returns the lowest-locale-code match (deterministic across\n\t * calls). Mirrors `TaxonomyRepository.findBySlug`.\n\t */\n\tasync findBySlug(slug: string, options?: { locale?: string }): Promise<BylineSummary | null> {\n\t\tlet query = this.db.selectFrom(\"_emdash_bylines\").selectAll().where(\"slug\", \"=\", slug);\n\t\tif (options?.locale !== undefined) query = query.where(\"locale\", \"=\", options.locale);\n\t\tconst row = await query.orderBy(\"locale\", \"asc\").executeTakeFirst();\n\t\treturn this.withCustomFieldsOne(row);\n\t}\n\n\t/**\n\t * Find the byline linked to a CMS user. Post-migration 040 the partial\n\t * unique on user_id is `(user_id, locale)`, so `locale` is required to\n\t * disambiguate when multiple locale variants exist. When omitted, returns\n\t * the lowest-locale-code match.\n\t */\n\tasync findByUserId(userId: string, options?: { locale?: string }): Promise<BylineSummary | null> {\n\t\tlet query = this.db.selectFrom(\"_emdash_bylines\").selectAll().where(\"user_id\", \"=\", userId);\n\t\tif (options?.locale !== undefined) query = query.where(\"locale\", \"=\", options.locale);\n\t\tconst row = await query.orderBy(\"locale\", \"asc\").executeTakeFirst();\n\t\treturn this.withCustomFieldsOne(row);\n\t}\n\n\tasync findMany(options?: {\n\t\tsearch?: string;\n\t\tisGuest?: boolean;\n\t\tuserId?: string;\n\t\tlocale?: string;\n\t\tcursor?: string;\n\t\tlimit?: number;\n\t}): Promise<FindManyResult<BylineSummary>> {\n\t\tconst limit = Math.min(Math.max(options?.limit ?? 50, 1), 100);\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(\"created_at\", \"desc\")\n\t\t\t.orderBy(\"id\", \"desc\")\n\t\t\t.limit(limit + 1);\n\n\t\tif (options?.search) {\n\t\t\tconst escaped = options.search\n\t\t\t\t.replaceAll(\"\\\\\", \"\\\\\\\\\")\n\t\t\t\t.replaceAll(\"%\", \"\\\\%\")\n\t\t\t\t.replaceAll(\"_\", \"\\\\_\");\n\t\t\tconst term = `%${escaped}%`;\n\t\t\tquery = query.where((eb) =>\n\t\t\t\teb.or([eb(\"display_name\", \"like\", term), eb(\"slug\", \"like\", term)]),\n\t\t\t);\n\t\t}\n\n\t\tif (options?.isGuest !== undefined) {\n\t\t\tquery = query.where(\"is_guest\", \"=\", options.isGuest ? 1 : 0);\n\t\t}\n\n\t\tif (options?.userId !== undefined) {\n\t\t\tquery = query.where(\"user_id\", \"=\", options.userId);\n\t\t}\n\n\t\tif (options?.locale !== undefined) {\n\t\t\tquery = query.where(\"locale\", \"=\", options.locale);\n\t\t}\n\n\t\tif (options?.cursor) {\n\t\t\tconst decoded = decodeCursor(options.cursor);\n\t\t\tquery = query.where((eb) =>\n\t\t\t\teb.or([\n\t\t\t\t\teb(\"created_at\", \"<\", decoded.orderValue),\n\t\t\t\t\teb.and([eb(\"created_at\", \"=\", decoded.orderValue), eb(\"id\", \"<\", decoded.id)]),\n\t\t\t\t]),\n\t\t\t);\n\t\t}\n\n\t\tconst rows = await query.execute();\n\t\tconst pageRows = rows.slice(0, limit);\n\t\tconst items = await this.withCustomFields(pageRows);\n\t\tconst result: FindManyResult<BylineSummary> = { items };\n\n\t\tif (rows.length > limit) {\n\t\t\tconst last = items.at(-1);\n\t\t\tif (last) {\n\t\t\t\tresult.nextCursor = encodeCursor(last.createdAt, last.id);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * List every sibling row in `translation_group`. Used by the admin\n\t * `TranslationsPanel` to render one entry per configured locale.\n\t */\n\tasync listTranslations(id: string): Promise<BylineSummary[]> {\n\t\tconst anchor = await this.findById(id);\n\t\tif (!anchor) return [];\n\t\tconst group = anchor.translationGroup ?? anchor.id;\n\t\treturn this.findByTranslationGroup(group);\n\t}\n\n\t/**\n\t * Direct lookup by `translation_group`. Returns every locale variant of a\n\t * byline, ordered by locale code (deterministic).\n\t */\n\tasync findByTranslationGroup(translationGroup: string): Promise<BylineSummary[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t.selectAll()\n\t\t\t.where(\"translation_group\", \"=\", translationGroup)\n\t\t\t.orderBy(\"locale\", \"asc\")\n\t\t\t.execute();\n\t\treturn this.withCustomFields(rows);\n\t}\n\n\t/**\n\t * Validate a `customFields` input map into a write list before any row\n\t * write — throws `EmDashValidationError` on unknown slugs, type\n\t * mismatches, or select-choice misses.\n\t */\n\tprivate async resolveCustomFieldWrites(\n\t\tcustomFields: Record<string, unknown> | undefined,\n\t): Promise<Array<{ field: BylineFieldDefinition; value: CustomFieldValue }>> {\n\t\tif (!customFields || Object.keys(customFields).length === 0) return [];\n\t\tconst defs = await getBylineFieldDefs(this.db);\n\t\tconst bySlug = new Map(defs.map((d) => [d.slug, d]));\n\t\tconst writes: Array<{ field: BylineFieldDefinition; value: CustomFieldValue }> = [];\n\t\tfor (const [slug, raw] of Object.entries(customFields)) {\n\t\t\tconst field = bySlug.get(slug);\n\t\t\tif (!field) {\n\t\t\t\tthrow new EmDashValidationError(`Unknown byline custom field \"${slug}\"`, {\n\t\t\t\t\tslug,\n\t\t\t\t\tregistered: defs.map((d) => d.slug),\n\t\t\t\t});\n\t\t\t}\n\t\t\twrites.push({ field, value: coerceFieldValue(field, raw) });\n\t\t}\n\t\treturn writes;\n\t}\n\n\t/**\n\t * Write a validated custom-field list against a byline row inside the\n\t * caller's transaction. Per-field writes route to\n\t * `_emdash_byline_field_values` (translatable) or\n\t * `_emdash_byline_field_group_values` (group-shared); `null` clears.\n\t * Returns `true` when any group-shared row was touched so the caller\n\t * can invalidate the per-request cache post-commit.\n\t */\n\tprivate async applyCustomFieldWritesInTrx(\n\t\ttrx: Kysely<Database>,\n\t\tbylineId: string,\n\t\ttranslationGroup: string,\n\t\twrites: Array<{ field: BylineFieldDefinition; value: CustomFieldValue }>,\n\t\tnow: string,\n\t): Promise<boolean> {\n\t\tif (writes.length === 0) return false;\n\t\tlet touchedGroupShared = false;\n\t\tfor (const { field, value } of writes) {\n\t\t\tif (!field.translatable) touchedGroupShared = true;\n\t\t\tif (field.translatable) {\n\t\t\t\tif (value === null) {\n\t\t\t\t\tawait trx\n\t\t\t\t\t\t.deleteFrom(\"_emdash_byline_field_values\")\n\t\t\t\t\t\t.where(\"byline_id\", \"=\", bylineId)\n\t\t\t\t\t\t.where(\"field_id\", \"=\", field.id)\n\t\t\t\t\t\t.execute();\n\t\t\t\t} else {\n\t\t\t\t\tconst encoded = JSON.stringify(value);\n\t\t\t\t\tawait trx\n\t\t\t\t\t\t.insertInto(\"_emdash_byline_field_values\")\n\t\t\t\t\t\t.values({\n\t\t\t\t\t\t\tbyline_id: bylineId,\n\t\t\t\t\t\t\tfield_id: field.id,\n\t\t\t\t\t\t\tvalue: encoded,\n\t\t\t\t\t\t\tcreated_at: now,\n\t\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.onConflict((oc) =>\n\t\t\t\t\t\t\toc.columns([\"byline_id\", \"field_id\"]).doUpdateSet({\n\t\t\t\t\t\t\t\tvalue: encoded,\n\t\t\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.execute();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (value === null) {\n\t\t\t\t\tawait trx\n\t\t\t\t\t\t.deleteFrom(\"_emdash_byline_field_group_values\")\n\t\t\t\t\t\t.where(\"translation_group\", \"=\", translationGroup)\n\t\t\t\t\t\t.where(\"field_id\", \"=\", field.id)\n\t\t\t\t\t\t.execute();\n\t\t\t\t} else {\n\t\t\t\t\tconst encoded = JSON.stringify(value);\n\t\t\t\t\tawait trx\n\t\t\t\t\t\t.insertInto(\"_emdash_byline_field_group_values\")\n\t\t\t\t\t\t.values({\n\t\t\t\t\t\t\ttranslation_group: translationGroup,\n\t\t\t\t\t\t\tfield_id: field.id,\n\t\t\t\t\t\t\tvalue: encoded,\n\t\t\t\t\t\t\tcreated_at: now,\n\t\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.onConflict((oc) =>\n\t\t\t\t\t\t\toc.columns([\"translation_group\", \"field_id\"]).doUpdateSet({\n\t\t\t\t\t\t\t\tvalue: encoded,\n\t\t\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.execute();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn touchedGroupShared;\n\t}\n\n\tasync create(input: CreateBylineInput): Promise<BylineSummary> {\n\t\tconst id = ulid();\n\t\tconst now = new Date().toISOString();\n\n\t\t// Validate customFields before opening the transaction so a bad\n\t\t// value surfaces as VALIDATION_ERROR without aborting an insert.\n\t\tconst customFieldWrites = await this.resolveCustomFieldWrites(input.customFields);\n\n\t\t// translationOf joins the source's group; otherwise mint a fresh\n\t\t// group = id (matches migration 040's backfill pattern).\n\t\tlet translationGroup: string = id;\n\t\tif (input.translationOf) {\n\t\t\tconst source = await this.findById(input.translationOf);\n\t\t\tif (!source) throw new Error(\"Source byline for translation not found\");\n\t\t\ttranslationGroup = source.translationGroup ?? source.id;\n\t\t}\n\n\t\t// Wrap insert + custom-field writes in one transaction so a\n\t\t// partial failure rolls both back on Node/PG. D1 still has its\n\t\t// own no-transactions limitation — recovery for that path lives\n\t\t// in `handleBylineCreate`.\n\t\tlet touchedGroupShared = false;\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tawait trx\n\t\t\t\t.insertInto(\"_emdash_bylines\")\n\t\t\t\t.values({\n\t\t\t\t\tid,\n\t\t\t\t\tslug: input.slug,\n\t\t\t\t\tdisplay_name: input.displayName,\n\t\t\t\t\tbio: input.bio ?? null,\n\t\t\t\t\tavatar_media_id: input.avatarMediaId ?? null,\n\t\t\t\t\twebsite_url: input.websiteUrl ?? null,\n\t\t\t\t\tuser_id: input.userId ?? null,\n\t\t\t\t\tis_guest: input.isGuest ? 1 : 0,\n\t\t\t\t\tcreated_at: now,\n\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t// Omit `locale` so the DB DEFAULT (configured defaultLocale)\n\t\t\t\t\t// applies — matches TaxonomyRepository.create.\n\t\t\t\t\t...(input.locale !== undefined ? { locale: input.locale } : {}),\n\t\t\t\t\ttranslation_group: translationGroup,\n\t\t\t\t})\n\t\t\t\t.execute();\n\n\t\t\ttouchedGroupShared = await this.applyCustomFieldWritesInTrx(\n\t\t\t\ttrx,\n\t\t\t\tid,\n\t\t\t\ttranslationGroup,\n\t\t\t\tcustomFieldWrites,\n\t\t\t\tnow,\n\t\t\t);\n\t\t});\n\n\t\tif (touchedGroupShared) {\n\t\t\tclearRequestCacheEntry(`byline-field-group-values:${translationGroup}`);\n\t\t}\n\n\t\tconst byline = await this.findById(id);\n\t\tif (!byline) {\n\t\t\tthrow new Error(\"Failed to create byline\");\n\t\t}\n\t\treturn byline;\n\t}\n\n\tasync update(id: string, input: UpdateBylineInput): Promise<BylineSummary | null> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) return null;\n\n\t\t// Validate customFields before opening the transaction so a bad\n\t\t// value surfaces as VALIDATION_ERROR without aborting an update.\n\t\tconst customFieldWrites = await this.resolveCustomFieldWrites(input.customFields);\n\n\t\tconst now = new Date().toISOString();\n\t\tconst updates: Record<string, unknown> = { updated_at: now };\n\n\t\tif (input.slug !== undefined) updates.slug = input.slug;\n\t\tif (input.displayName !== undefined) updates.display_name = input.displayName;\n\t\tif (input.bio !== undefined) updates.bio = input.bio;\n\t\tif (input.avatarMediaId !== undefined) updates.avatar_media_id = input.avatarMediaId;\n\t\tif (input.websiteUrl !== undefined) updates.website_url = input.websiteUrl;\n\t\tif (input.userId !== undefined) updates.user_id = input.userId;\n\t\tif (input.isGuest !== undefined) updates.is_guest = input.isGuest ? 1 : 0;\n\n\t\tconst group = existing.translationGroup ?? existing.id;\n\t\t// Wrap row update + custom-field writes in one transaction so a\n\t\t// partial failure rolls both back on Node/PG. The post-commit\n\t\t// invalidation below clears the per-request cache that the\n\t\t// top-of-method `findById` populated for this group.\n\t\tlet touchedGroupShared = false;\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tawait trx.updateTable(\"_emdash_bylines\").set(updates).where(\"id\", \"=\", id).execute();\n\t\t\ttouchedGroupShared = await this.applyCustomFieldWritesInTrx(\n\t\t\t\ttrx,\n\t\t\t\tid,\n\t\t\t\tgroup,\n\t\t\t\tcustomFieldWrites,\n\t\t\t\tnow,\n\t\t\t);\n\t\t});\n\n\t\tif (touchedGroupShared) {\n\t\t\tclearRequestCacheEntry(`byline-field-group-values:${group}`);\n\t\t}\n\n\t\treturn await this.findById(id);\n\t}\n\n\t/**\n\t * Delete a byline row. When this row is the last sibling in its\n\t * translation group, also drops every junction row pointing at the group,\n\t * clears `primary_byline_id` references, and removes the byline's\n\t * non-translatable custom-field values. When other siblings remain in\n\t * the group, junctions, `primary_byline_id` pointers, and group-shared\n\t * custom-field values stay intact — the credit (and its shared metadata)\n\t * lives on at other locales.\n\t *\n\t * **Application-level cascade.** The byline domain has standardised on\n\t * app-level cascade rather than trusting FK ON DELETE CASCADE, partly\n\t * because migration 040 had to strip its own FK to support the\n\t * translation_group remap (#1021), and partly so cleanup doesn't\n\t * depend on `PRAGMA foreign_keys = ON` (set in production via\n\t * `connection.ts:60`, but easy to bypass in tests, scripts, and\n\t * one-off tools). Every byline-related deletion table is cleared\n\t * explicitly here:\n\t *\n\t * - `_emdash_byline_field_values` (per-byline translatable values) —\n\t * migration 041 declares FK ON DELETE CASCADE on `byline_id`; the\n\t * explicit DELETE removes the dependency on that pragma.\n\t * - `_emdash_content_bylines` — migration 040 dropped its FK.\n\t * - `ec_*.primary_byline_id` — never had an FK.\n\t * - `_emdash_byline_field_group_values` (translation-group-keyed) —\n\t * keyed by a text column with no FK to bylines, so app-level cleanup\n\t * is the only path.\n\t *\n\t * The FKs that remain (migration 041) serve as defense-in-depth.\n\t */\n\tasync delete(id: string): Promise<boolean> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) return false;\n\n\t\tconst group = existing.translationGroup ?? existing.id;\n\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\t// Per-row translatable custom-field values. Done BEFORE the\n\t\t\t// byline row delete so the application-level cleanup is\n\t\t\t// observable in the transaction log even if FK enforcement is\n\t\t\t// off; migration 041's FK ON DELETE CASCADE would catch any\n\t\t\t// row we miss, but the explicit DELETE is what the rest of\n\t\t\t// the byline domain expects to see.\n\t\t\tawait trx.deleteFrom(\"_emdash_byline_field_values\").where(\"byline_id\", \"=\", id).execute();\n\n\t\t\tawait trx.deleteFrom(\"_emdash_bylines\").where(\"id\", \"=\", id).execute();\n\n\t\t\t// Count remaining siblings in the translation group. If none\n\t\t\t// remain, purge dependent rows; otherwise leave them intact so\n\t\t\t// the credit still resolves at other locales.\n\t\t\tconst remaining = await trx\n\t\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t\t.select(({ fn }) => [fn.count<number>(\"id\").as(\"count\")])\n\t\t\t\t.where(\"translation_group\", \"=\", group)\n\t\t\t\t.executeTakeFirst();\n\t\t\tconst remainingCount = Number(remaining?.count ?? 0);\n\t\t\tif (remainingCount > 0) return;\n\n\t\t\t// Last sibling gone: cascade in application code.\n\t\t\tawait trx.deleteFrom(\"_emdash_content_bylines\").where(\"byline_id\", \"=\", group).execute();\n\n\t\t\t// Group-shared custom-field values are keyed by translation_group\n\t\t\t// (no FK to bylines), so they don't cascade with the byline row.\n\t\t\t// Clean them up explicitly so deleting the last sibling of an\n\t\t\t// identity doesn't leave orphan group values pointing at a\n\t\t\t// vanished translation group. Per-row translatable values\n\t\t\t// (`_emdash_byline_field_values` keyed by byline_id) already\n\t\t\t// cascaded when each sibling row was deleted, so no extra\n\t\t\t// cleanup is needed for that table.\n\t\t\tawait trx\n\t\t\t\t.deleteFrom(\"_emdash_byline_field_group_values\")\n\t\t\t\t.where(\"translation_group\", \"=\", group)\n\t\t\t\t.execute();\n\n\t\t\tconst tableNames = await listTablesLike(trx, \"ec_%\");\n\t\t\tfor (const tableName of tableNames) {\n\t\t\t\tvalidateIdentifier(tableName, \"content table\");\n\t\t\t\tawait sql`\n\t\t\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\t\t\tSET primary_byline_id = NULL\n\t\t\t\t\tWHERE primary_byline_id = ${group}\n\t\t\t\t`.execute(trx);\n\t\t\t}\n\t\t});\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Strict per-locale credit hydration. Joins `_emdash_content_bylines` to\n\t * `_emdash_bylines` on `translation_group = byline_id`, then filters to\n\t * the requested locale. Credits whose translation group lacks a row at\n\t * the requested locale are omitted — callers wanting fallback behaviour\n\t * apply it themselves. Mirrors `TaxonomyRepository.getTermsForEntry`.\n\t */\n\tasync getContentBylines(\n\t\tcollectionSlug: string,\n\t\tcontentId: string,\n\t\toptions?: { locale?: string },\n\t): Promise<ContentBylineCredit[]> {\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_content_bylines as cb\")\n\t\t\t.innerJoin(\"_emdash_bylines as b\", \"b.translation_group\", \"cb.byline_id\")\n\t\t\t.leftJoin(\"media as m\", \"m.id\", \"b.avatar_media_id\")\n\t\t\t.select([\n\t\t\t\t\"cb.sort_order as sort_order\",\n\t\t\t\t\"cb.role_label as role_label\",\n\t\t\t\t\"b.id as id\",\n\t\t\t\t\"b.slug as slug\",\n\t\t\t\t\"b.display_name as display_name\",\n\t\t\t\t\"b.bio as bio\",\n\t\t\t\t\"b.avatar_media_id as avatar_media_id\",\n\t\t\t\t\"m.storage_key as avatar_storage_key\",\n\t\t\t\t\"m.alt as avatar_alt\",\n\t\t\t\t\"b.website_url as website_url\",\n\t\t\t\t\"b.user_id as user_id\",\n\t\t\t\t\"b.is_guest as is_guest\",\n\t\t\t\t\"b.created_at as created_at\",\n\t\t\t\t\"b.updated_at as updated_at\",\n\t\t\t\t\"b.locale as locale\",\n\t\t\t\t\"b.translation_group as translation_group\",\n\t\t\t])\n\t\t\t.where(\"cb.collection_slug\", \"=\", collectionSlug)\n\t\t\t.where(\"cb.content_id\", \"=\", contentId)\n\t\t\t.orderBy(\"cb.sort_order\", \"asc\");\n\t\tif (options?.locale !== undefined) query = query.where(\"b.locale\", \"=\", options.locale);\n\n\t\tconst rows = await query.execute();\n\t\t// Reconstruct byline rows to feed `withCustomFields`. The JOIN selects\n\t\t// the `BylineRow` columns under the `b.` alias plus the avatar media\n\t\t// columns from the `media` LEFT JOIN; carry both through so\n\t\t// `rowToByline` can populate `avatarStorageKey`/`avatarAlt` (otherwise\n\t\t// the join runs but its values are dropped here).\n\t\tconst bylineRows: BylineRowWithAvatar[] = rows.map((row) => ({\n\t\t\tid: row.id,\n\t\t\tslug: row.slug,\n\t\t\tdisplay_name: row.display_name,\n\t\t\tbio: row.bio,\n\t\t\tavatar_media_id: row.avatar_media_id,\n\t\t\tavatar_storage_key: row.avatar_storage_key,\n\t\t\tavatar_alt: row.avatar_alt,\n\t\t\twebsite_url: row.website_url,\n\t\t\tuser_id: row.user_id,\n\t\t\tis_guest: row.is_guest,\n\t\t\tcreated_at: row.created_at,\n\t\t\tupdated_at: row.updated_at,\n\t\t\tlocale: row.locale,\n\t\t\ttranslation_group: row.translation_group,\n\t\t}));\n\t\tconst hydrated = await this.withCustomFields(bylineRows);\n\t\treturn rows.map((row, i) => {\n\t\t\tconst byline = hydrated[i];\n\t\t\tif (!byline) {\n\t\t\t\t// Defensive: hydrated and rows are produced in lock-step;\n\t\t\t\t// this branch is unreachable unless `withCustomFields`\n\t\t\t\t// breaks its contract.\n\t\t\t\tthrow new Error(\"getContentBylines: hydration row count mismatch\");\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tbyline,\n\t\t\t\tsortOrder: row.sort_order,\n\t\t\t\troleLabel: row.role_label,\n\t\t\t};\n\t\t});\n\t}\n\n\t/**\n\t * Does this entry have any explicit byline credits — at any locale?\n\t *\n\t * Used to disambiguate \"no credits exist\" (fall back to author-linked\n\t * byline) from \"credits exist but don't resolve at the requested locale\"\n\t * (strict per-locale model: render no byline). Without this check the\n\t * locale-strict hydration would silently turn a missing translation into\n\t * an author-inferred byline, contradicting editorial intent.\n\t */\n\tasync hasContentBylines(collectionSlug: string, contentId: string): Promise<boolean> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_content_bylines\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"collection_slug\", \"=\", collectionSlug)\n\t\t\t.where(\"content_id\", \"=\", contentId)\n\t\t\t.limit(1)\n\t\t\t.executeTakeFirst();\n\t\treturn row !== undefined;\n\t}\n\n\t/**\n\t * Batch variant of `hasContentBylines`. Returns the set of content IDs\n\t * that have at least one junction row (locale-agnostic).\n\t */\n\tasync hasContentBylinesMany(collectionSlug: string, contentIds: string[]): Promise<Set<string>> {\n\t\tconst result = new Set<string>();\n\t\tif (contentIds.length === 0) return result;\n\n\t\tconst uniqueContentIds = [...new Set(contentIds)];\n\t\tfor (const chunk of chunks(uniqueContentIds, SQL_BATCH_SIZE)) {\n\t\t\tconst rows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_content_bylines\")\n\t\t\t\t.select(\"content_id\")\n\t\t\t\t.distinct()\n\t\t\t\t.where(\"collection_slug\", \"=\", collectionSlug)\n\t\t\t\t.where(\"content_id\", \"in\", chunk)\n\t\t\t\t.execute();\n\t\t\tfor (const row of rows) result.add(row.content_id);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Batch variant of `getContentBylines`. Same strict-per-locale semantics\n\t * applied to the requested locale (single value, not per-entry).\n\t *\n\t * When callers need per-entry-locale filtering (e.g. a list endpoint\n\t * returning entries at mixed locales), they should group the input ids by\n\t * the entry's locale and call this method once per group.\n\t *\n\t * When the caller will issue multiple `getContentBylinesMany` calls in\n\t * one request (e.g. per locale bucket) and wants a *single* batched\n\t * customFields hydration over the union of returned bylines, pass\n\t * `skipHydration: true` on each call and finish with\n\t * `hydrateBylineCustomFields(allBylines)`. The returned bylines carry\n\t * `customFields = {}` until that hydration call runs — matching the\n\t * \"always populated\" invariant from AC #6 — so callers that forget to\n\t * hydrate get an empty map rather than `undefined`.\n\t */\n\tasync getContentBylinesMany(\n\t\tcollectionSlug: string,\n\t\tcontentIds: string[],\n\t\toptions?: { locale?: string; skipHydration?: boolean },\n\t): Promise<Map<string, ContentBylineCredit[]>> {\n\t\tconst result = new Map<string, ContentBylineCredit[]>();\n\t\tif (contentIds.length === 0) return result;\n\n\t\tconst uniqueContentIds = [...new Set(contentIds)];\n\t\tfor (const chunk of chunks(uniqueContentIds, SQL_BATCH_SIZE)) {\n\t\t\tlet query = this.db\n\t\t\t\t.selectFrom(\"_emdash_content_bylines as cb\")\n\t\t\t\t.innerJoin(\"_emdash_bylines as b\", \"b.translation_group\", \"cb.byline_id\")\n\t\t\t\t.leftJoin(\"media as m\", \"m.id\", \"b.avatar_media_id\")\n\t\t\t\t.select([\n\t\t\t\t\t\"cb.content_id as content_id\",\n\t\t\t\t\t\"cb.sort_order as sort_order\",\n\t\t\t\t\t\"cb.role_label as role_label\",\n\t\t\t\t\t\"b.id as id\",\n\t\t\t\t\t\"b.slug as slug\",\n\t\t\t\t\t\"b.display_name as display_name\",\n\t\t\t\t\t\"b.bio as bio\",\n\t\t\t\t\t\"b.avatar_media_id as avatar_media_id\",\n\t\t\t\t\t\"m.storage_key as avatar_storage_key\",\n\t\t\t\t\t\"m.alt as avatar_alt\",\n\t\t\t\t\t\"b.website_url as website_url\",\n\t\t\t\t\t\"b.user_id as user_id\",\n\t\t\t\t\t\"b.is_guest as is_guest\",\n\t\t\t\t\t\"b.created_at as created_at\",\n\t\t\t\t\t\"b.updated_at as updated_at\",\n\t\t\t\t\t\"b.locale as locale\",\n\t\t\t\t\t\"b.translation_group as translation_group\",\n\t\t\t\t])\n\t\t\t\t.where(\"cb.collection_slug\", \"=\", collectionSlug)\n\t\t\t\t.where(\"cb.content_id\", \"in\", chunk)\n\t\t\t\t.orderBy(\"cb.sort_order\", \"asc\");\n\t\t\tif (options?.locale !== undefined) query = query.where(\"b.locale\", \"=\", options.locale);\n\n\t\t\tconst rows = await query.execute();\n\t\t\t// Carry the avatar media columns from the LEFT JOIN through the\n\t\t\t// reshape so `rowToByline` can populate avatarStorageKey/avatarAlt.\n\t\t\tconst bylineRows: BylineRowWithAvatar[] = rows.map((row) => ({\n\t\t\t\tid: row.id,\n\t\t\t\tslug: row.slug,\n\t\t\t\tdisplay_name: row.display_name,\n\t\t\t\tbio: row.bio,\n\t\t\t\tavatar_media_id: row.avatar_media_id,\n\t\t\t\tavatar_storage_key: row.avatar_storage_key,\n\t\t\t\tavatar_alt: row.avatar_alt,\n\t\t\t\twebsite_url: row.website_url,\n\t\t\t\tuser_id: row.user_id,\n\t\t\t\tis_guest: row.is_guest,\n\t\t\t\tcreated_at: row.created_at,\n\t\t\t\tupdated_at: row.updated_at,\n\t\t\t\tlocale: row.locale,\n\t\t\t\ttranslation_group: row.translation_group,\n\t\t\t}));\n\n\t\t\t// When `skipHydration` is set, return BylineSummary objects with\n\t\t\t// `customFields = {}`. The caller is responsible for batching\n\t\t\t// `hydrateBylineCustomFields` across multiple\n\t\t\t// `getContentBylinesMany` calls. Otherwise hydrate per-call —\n\t\t\t// the historical behaviour for solo callers.\n\t\t\tlet bylines: BylineSummary[];\n\t\t\tif (options?.skipHydration === true) {\n\t\t\t\tbylines = bylineRows.map(rowToByline);\n\t\t\t\tfor (const b of bylines) b.customFields = {};\n\t\t\t} else {\n\t\t\t\tbylines = await this.withCustomFields(bylineRows);\n\t\t\t}\n\n\t\t\tfor (let i = 0; i < rows.length; i++) {\n\t\t\t\tconst row = rows[i];\n\t\t\t\tconst byline = bylines[i];\n\t\t\t\tif (!row || !byline) continue;\n\t\t\t\tconst contentId = row.content_id;\n\t\t\t\tconst credit: ContentBylineCredit = {\n\t\t\t\t\tbyline,\n\t\t\t\t\tsortOrder: row.sort_order,\n\t\t\t\t\troleLabel: row.role_label,\n\t\t\t\t};\n\t\t\t\tconst existing = result.get(contentId);\n\t\t\t\tif (existing) {\n\t\t\t\t\texisting.push(credit);\n\t\t\t\t} else {\n\t\t\t\t\tresult.set(contentId, [credit]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Batch-fetch byline profiles linked to user IDs in a single query.\n\t * Strict-locale variant of `findByUserId`.\n\t *\n\t * `skipHydration: true` returns bylines with `customFields = {}` so\n\t * callers issuing multiple `findByUserIds` calls in one request (e.g.\n\t * the per-locale-bucket author-fallback path in `getBylinesForEntries`)\n\t * can defer customFields hydration to a single batched\n\t * `hydrateBylineCustomFields` call across the union — keeping the\n\t * Phase 3 query-count envelope at \"+1 group-shared query per\n\t * hydration pass\" even when buckets fetch disjoint author bylines.\n\t */\n\tasync findByUserIds(\n\t\tuserIds: string[],\n\t\toptions?: { locale?: string; skipHydration?: boolean },\n\t): Promise<Map<string, BylineSummary>> {\n\t\tconst result = new Map<string, BylineSummary>();\n\t\tif (userIds.length === 0) return result;\n\n\t\tfor (const chunk of chunks(userIds, SQL_BATCH_SIZE)) {\n\t\t\t// LEFT JOIN media so author-inferred bylines (the fallback path in\n\t\t\t// `getBylinesForEntries`) carry the same render-ready avatar storage\n\t\t\t// key as explicitly-credited bylines do.\n\t\t\tlet query = this.db\n\t\t\t\t.selectFrom(\"_emdash_bylines as b\")\n\t\t\t\t.leftJoin(\"media as m\", \"m.id\", \"b.avatar_media_id\")\n\t\t\t\t.select([\n\t\t\t\t\t\"b.id as id\",\n\t\t\t\t\t\"b.slug as slug\",\n\t\t\t\t\t\"b.display_name as display_name\",\n\t\t\t\t\t\"b.bio as bio\",\n\t\t\t\t\t\"b.avatar_media_id as avatar_media_id\",\n\t\t\t\t\t\"m.storage_key as avatar_storage_key\",\n\t\t\t\t\t\"m.alt as avatar_alt\",\n\t\t\t\t\t\"b.website_url as website_url\",\n\t\t\t\t\t\"b.user_id as user_id\",\n\t\t\t\t\t\"b.is_guest as is_guest\",\n\t\t\t\t\t\"b.created_at as created_at\",\n\t\t\t\t\t\"b.updated_at as updated_at\",\n\t\t\t\t\t\"b.locale as locale\",\n\t\t\t\t\t\"b.translation_group as translation_group\",\n\t\t\t\t])\n\t\t\t\t.where(\"b.user_id\", \"in\", chunk);\n\t\t\tif (options?.locale !== undefined) query = query.where(\"b.locale\", \"=\", options.locale);\n\n\t\t\tconst rows = await query.execute();\n\t\t\tlet bylines: BylineSummary[];\n\t\t\tif (options?.skipHydration === true) {\n\t\t\t\tbylines = rows.map(rowToByline);\n\t\t\t\tfor (const b of bylines) b.customFields = {};\n\t\t\t} else {\n\t\t\t\tbylines = await this.withCustomFields(rows);\n\t\t\t}\n\n\t\t\tfor (let i = 0; i < rows.length; i++) {\n\t\t\t\tconst row = rows[i];\n\t\t\t\tconst summary = bylines[i];\n\t\t\t\tif (!row || !summary || !row.user_id) continue;\n\t\t\t\tresult.set(row.user_id, summary);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Clone every junction row from `sourceContentId` to `targetContentId`,\n\t * preserving `sort_order` and `role_label`. Used by the content\n\t * translation flow: a newly created translation inherits the source's\n\t * byline credits at the storage level. Because the junction stores\n\t * `translation_group` (not a row id), the copy is locale-agnostic — the\n\t * credits resolve to whichever locale variants of each byline exist when\n\t * the translated entry is hydrated.\n\t *\n\t * No-op when the source has no credits. Skips when the target already\n\t * has credits (idempotent for re-runs).\n\t */\n\tasync copyContentBylines(\n\t\tcollection: string,\n\t\tsourceContentId: string,\n\t\ttargetContentId: string,\n\t): Promise<void> {\n\t\tvalidateIdentifier(collection, \"collection slug\");\n\t\tconst tableName = `ec_${collection}`;\n\t\tvalidateIdentifier(tableName, \"content table\");\n\n\t\t// Like `setContentBylines`, this method is expected to be called\n\t\t// within a transaction context (content handlers wrap in\n\t\t// withTransaction). All operations use `this.db` directly so an\n\t\t// outer transaction can serialise the copy alongside the create.\n\t\tconst existing = await this.db\n\t\t\t.selectFrom(\"_emdash_content_bylines\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"collection_slug\", \"=\", collection)\n\t\t\t.where(\"content_id\", \"=\", targetContentId)\n\t\t\t.executeTakeFirst();\n\t\tif (existing) return;\n\n\t\tconst sourceRows = await this.db\n\t\t\t.selectFrom(\"_emdash_content_bylines\")\n\t\t\t.select([\"byline_id\", \"sort_order\", \"role_label\"])\n\t\t\t.where(\"collection_slug\", \"=\", collection)\n\t\t\t.where(\"content_id\", \"=\", sourceContentId)\n\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t.execute();\n\t\tif (sourceRows.length === 0) return;\n\n\t\tconst now = new Date().toISOString();\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_content_bylines\")\n\t\t\t.values(\n\t\t\t\tsourceRows.map((row) => ({\n\t\t\t\t\tid: ulid(),\n\t\t\t\t\tcollection_slug: collection,\n\t\t\t\t\tcontent_id: targetContentId,\n\t\t\t\t\tbyline_id: row.byline_id,\n\t\t\t\t\tsort_order: row.sort_order,\n\t\t\t\t\trole_label: row.role_label,\n\t\t\t\t\tcreated_at: now,\n\t\t\t\t})),\n\t\t\t)\n\t\t\t.execute();\n\n\t\t// Mirror primary_byline_id from source so the cached pointer on the\n\t\t// target row matches the junction state we just wrote.\n\t\tconst firstByline = sourceRows[0]?.byline_id ?? null;\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET primary_byline_id = ${firstByline}\n\t\t\tWHERE id = ${targetContentId}\n\t\t`.execute(this.db);\n\t}\n\n\t/**\n\t * Replace the set of byline credits on a content entry. Accepts row ids\n\t * at the wire (consistent with how the admin sends them), translates\n\t * each to its `translation_group` on write, and stores the group in\n\t * `_emdash_content_bylines.byline_id` and `ec_*.primary_byline_id`.\n\t *\n\t * The returned credits are hydrated with strict-locale matching at the\n\t * locale of the rows the caller supplied (i.e. the locale of the byline\n\t * each `bylineId` resolves to) — adequate for the autosave round-trip,\n\t * which then re-hydrates the entry against its own locale separately.\n\t */\n\tasync setContentBylines(\n\t\tcollectionSlug: string,\n\t\tcontentId: string,\n\t\tinputBylines: ContentBylineInput[],\n\t): Promise<ContentBylineCredit[]> {\n\t\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\t\tconst tableName = `ec_${collectionSlug}`;\n\t\tvalidateIdentifier(tableName, \"content table\");\n\n\t\t// Resolve each wire row id to its translation_group up front so we\n\t\t// can (a) validate the rows exist and (b) dedupe by the value that\n\t\t// actually lands in the junction. Deduping by wire row id BEFORE\n\t\t// resolving would let two locale siblings of the same byline slip\n\t\t// through and trigger a UNIQUE(collection, content, byline_id)\n\t\t// failure at insert time. A single SELECT keeps this O(1) DB\n\t\t// calls regardless of how many credits are being set.\n\t\tconst idToGroup = new Map<string, string>();\n\t\tif (inputBylines.length > 0) {\n\t\t\tconst wireIds = [...new Set(inputBylines.map((item) => item.bylineId))];\n\t\t\tconst rows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_bylines\")\n\t\t\t\t.select([\"id\", \"translation_group\"])\n\t\t\t\t.where(\"id\", \"in\", wireIds)\n\t\t\t\t.execute();\n\t\t\tif (rows.length !== wireIds.length) {\n\t\t\t\tthrow new Error(\"One or more byline IDs do not exist\");\n\t\t\t}\n\t\t\tfor (const row of rows) {\n\t\t\t\tidToGroup.set(row.id, row.translation_group ?? row.id);\n\t\t\t}\n\t\t}\n\n\t\t// Dedupe by translation_group. Preserves the order of first\n\t\t// occurrence so the editor's intent (which sibling appears first)\n\t\t// is honored. `roleLabel` follows the first occurrence too.\n\t\tconst seenGroups = new Set<string>();\n\t\tconst bylines: Array<ContentBylineInput & { group: string }> = [];\n\t\tfor (const item of inputBylines) {\n\t\t\tconst group = idToGroup.get(item.bylineId);\n\t\t\tif (!group) {\n\t\t\t\tthrow new Error(`Missing translation_group for byline ${item.bylineId}`);\n\t\t\t}\n\t\t\tif (seenGroups.has(group)) continue;\n\t\t\tseenGroups.add(group);\n\t\t\tbylines.push({ ...item, group });\n\t\t}\n\n\t\t// This method is expected to be called within a transaction context\n\t\t// (content handlers wrap in withTransaction, seed applies sequentially).\n\t\t// All operations use this.db directly -- callers are responsible for\n\t\t// wrapping in a transaction when atomicity is required.\n\t\tawait this.db\n\t\t\t.deleteFrom(\"_emdash_content_bylines\")\n\t\t\t.where(\"collection_slug\", \"=\", collectionSlug)\n\t\t\t.where(\"content_id\", \"=\", contentId)\n\t\t\t.execute();\n\n\t\tfor (let i = 0; i < bylines.length; i++) {\n\t\t\tconst item = bylines[i];\n\t\t\tif (!item) continue;\n\t\t\tawait this.db\n\t\t\t\t.insertInto(\"_emdash_content_bylines\")\n\t\t\t\t.values({\n\t\t\t\t\tid: ulid(),\n\t\t\t\t\tcollection_slug: collectionSlug,\n\t\t\t\t\tcontent_id: contentId,\n\t\t\t\t\tbyline_id: item.group,\n\t\t\t\t\tsort_order: i,\n\t\t\t\t\trole_label: item.roleLabel ?? null,\n\t\t\t\t\tcreated_at: new Date().toISOString(),\n\t\t\t\t})\n\t\t\t\t.execute();\n\t\t}\n\n\t\tconst primaryGroup = bylines[0]?.group ?? null;\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(tableName)}\n\t\t\tSET primary_byline_id = ${primaryGroup}\n\t\t\tWHERE id = ${contentId}\n\t\t`.execute(this.db);\n\n\t\treturn await this.getContentBylines(collectionSlug, contentId);\n\t}\n}\n"],"mappings":"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"}
@@ -590,14 +590,14 @@ declare const createFieldBody: z.ZodObject<{
590
590
  string: "string";
591
591
  number: "number";
592
592
  boolean: "boolean";
593
- slug: "slug";
594
- text: "text";
595
- url: "url";
596
- select: "select";
597
593
  file: "file";
594
+ url: "url";
598
595
  image: "image";
596
+ slug: "slug";
599
597
  datetime: "datetime";
598
+ text: "text";
600
599
  integer: "integer";
600
+ select: "select";
601
601
  multiSelect: "multiSelect";
602
602
  portableText: "portableText";
603
603
  reference: "reference";
@@ -621,10 +621,10 @@ declare const createFieldBody: z.ZodObject<{
621
621
  string: "string";
622
622
  number: "number";
623
623
  boolean: "boolean";
624
- text: "text";
625
- select: "select";
626
624
  datetime: "datetime";
625
+ text: "text";
627
626
  integer: "integer";
627
+ select: "select";
628
628
  }>;
629
629
  label: z.ZodString;
630
630
  required: z.ZodOptional<z.ZodBoolean>;
@@ -659,10 +659,10 @@ declare const updateFieldBody: z.ZodObject<{
659
659
  string: "string";
660
660
  number: "number";
661
661
  boolean: "boolean";
662
- text: "text";
663
- select: "select";
664
662
  datetime: "datetime";
663
+ text: "text";
665
664
  integer: "integer";
665
+ select: "select";
666
666
  }>;
667
667
  label: z.ZodString;
668
668
  required: z.ZodOptional<z.ZodBoolean>;
@@ -715,14 +715,14 @@ declare const fieldSchema: z.ZodObject<{
715
715
  string: "string";
716
716
  number: "number";
717
717
  boolean: "boolean";
718
- slug: "slug";
719
- text: "text";
720
- url: "url";
721
- select: "select";
722
718
  file: "file";
719
+ url: "url";
723
720
  image: "image";
721
+ slug: "slug";
724
722
  datetime: "datetime";
723
+ text: "text";
725
724
  integer: "integer";
725
+ select: "select";
726
726
  multiSelect: "multiSelect";
727
727
  portableText: "portableText";
728
728
  reference: "reference";
@@ -780,14 +780,14 @@ declare const collectionWithFieldsResponseSchema: z.ZodObject<{
780
780
  string: "string";
781
781
  number: "number";
782
782
  boolean: "boolean";
783
- slug: "slug";
784
- text: "text";
785
- url: "url";
786
- select: "select";
787
783
  file: "file";
784
+ url: "url";
788
785
  image: "image";
786
+ slug: "slug";
789
787
  datetime: "datetime";
788
+ text: "text";
790
789
  integer: "integer";
790
+ select: "select";
791
791
  multiSelect: "multiSelect";
792
792
  portableText: "portableText";
793
793
  reference: "reference";
@@ -834,14 +834,14 @@ declare const fieldResponseSchema: z.ZodObject<{
834
834
  string: "string";
835
835
  number: "number";
836
836
  boolean: "boolean";
837
- slug: "slug";
838
- text: "text";
839
- url: "url";
840
- select: "select";
841
837
  file: "file";
838
+ url: "url";
842
839
  image: "image";
840
+ slug: "slug";
843
841
  datetime: "datetime";
842
+ text: "text";
844
843
  integer: "integer";
844
+ select: "select";
845
845
  multiSelect: "multiSelect";
846
846
  portableText: "portableText";
847
847
  reference: "reference";
@@ -871,14 +871,14 @@ declare const fieldListResponseSchema: z.ZodObject<{
871
871
  string: "string";
872
872
  number: "number";
873
873
  boolean: "boolean";
874
- slug: "slug";
875
- text: "text";
876
- url: "url";
877
- select: "select";
878
874
  file: "file";
875
+ url: "url";
879
876
  image: "image";
877
+ slug: "slug";
880
878
  datetime: "datetime";
879
+ text: "text";
881
880
  integer: "integer";
881
+ select: "select";
882
882
  multiSelect: "multiSelect";
883
883
  portableText: "portableText";
884
884
  reference: "reference";
@@ -2053,8 +2053,8 @@ declare const bylineFieldCreateBody: z.ZodObject<{
2053
2053
  type: z.ZodEnum<{
2054
2054
  string: "string";
2055
2055
  boolean: "boolean";
2056
- text: "text";
2057
2056
  url: "url";
2057
+ text: "text";
2058
2058
  select: "select";
2059
2059
  }>;
2060
2060
  required: z.ZodOptional<z.ZodBoolean>;
@@ -2089,8 +2089,8 @@ declare const bylineFieldDefinitionSchema: z.ZodObject<{
2089
2089
  type: z.ZodEnum<{
2090
2090
  string: "string";
2091
2091
  boolean: "boolean";
2092
- text: "text";
2093
2092
  url: "url";
2093
+ text: "text";
2094
2094
  select: "select";
2095
2095
  }>;
2096
2096
  required: z.ZodBoolean;
@@ -2110,8 +2110,8 @@ declare const bylineFieldListResponseSchema: z.ZodObject<{
2110
2110
  type: z.ZodEnum<{
2111
2111
  string: "string";
2112
2112
  boolean: "boolean";
2113
- text: "text";
2114
2113
  url: "url";
2114
+ text: "text";
2115
2115
  select: "select";
2116
2116
  }>;
2117
2117
  required: z.ZodBoolean;
@@ -2141,4 +2141,4 @@ declare const bylineFieldUsageResponseSchema: z.ZodObject<{
2141
2141
  }, z.core.$strip>;
2142
2142
  //#endregion
2143
2143
  export { wpRewriteUrlsBody as $, apiErrorSchema as $n, commentSchema as $t, reorderWidgetsBody as A, mediaResponseSchema as An, menuSchema as At, userSchema as B, contentPublishBody as Bn, magicLinkSendBody as Bt, notFoundSummarySchema as C, mediaConfirmBody as Cn, termWithCountSchema as Ct, updateRedirectBody as D, mediaListQuery as Dn, menuItemSchema as Dt, redirectsListQuery as E, mediaItemSchema as En, createMenuItemBody as Et, widgetSchema as F, contentCreateBody as Fn, updateMenuItemBody as Ft, setupAdminVerifyBody as G, contentTermsBody as Gn, passkeyVerifyBody as Gt, usersListQuery as H, contentScheduleBody as Hn, passkeyRegisterOptionsBody as Ht, allowedDomainCreateBody as I, contentItemSchema as In, authMeActionBody as It, importProbeBody as J, contentTrashQuery as Jn, adminCommentListResponseSchema as Jt, setupAtprotoAdminBody as K, contentTranslationSchema as Kn, signupCompleteBody as Kt, allowedDomainUpdateBody as L, contentListQuery as Ln, inviteCompleteBody as Lt, widgetAreaSchema as M, mediaUploadUrlBody as Mn, menuWithItemsSchema as Mt, widgetAreaWithWidgetsAndCountSchema as N, mediaUploadUrlResponseSchema as Nn, reorderMenuItemsBody as Nt, createWidgetAreaBody as O, mediaListResponseSchema as On, menuItemTypeEnum as Ot, widgetAreaWithWidgetsSchema as P, contentCompareResponseSchema as Pn, updateMenuBody as Pt, wpPrepareBody as Q, VALID_ROLE_LEVELS as Qn, commentListQuery as Qt, userDetailSchema as R, contentListResponseSchema as Rn, inviteCreateBody as Rt, notFoundSummaryResponseSchema as S, formatFileSize as Sn, termTranslationsSchema as St, redirectSchema as T, mediaExistingResponseSchema as Tn, createMenuBody as Tt, atprotoLoginBody as U, contentSeoInput as Un, passkeyRegisterVerifyBody as Ut, userUpdateBody as V, contentResponseSchema as Vn, passkeyOptionsBody as Vt, setupAdminBody as W, contentSeoSchema as Wn, passkeyRenameBody as Wt, wpPluginAnalyzeBody as X, trashedContentItemSchema as Xn, commentBulkResponseSchema as Xt, wpMediaImportBody as Y, contentUpdateBody as Yn, commentBulkBody as Yt, wpPluginExecuteBody as Z, trashedContentListResponseSchema as Zn, commentCountsResponseSchema as Zt, notFoundEntrySchema as _, orphanedTableSchema as _n, taxonomyListResponseSchema as _t, bylineFieldUpdateBody as a, collectionListResponseSchema as an, localeFilterQuery as ar, searchSuggestQuery as at, notFoundPruneBody as b, updateFieldBody as bn, termResponseSchema as bt, bylineCreditSchema as c, collectionWithFieldsResponseSchema as cn, slugPattern as cr, createSectionBody as ct, bylineTranslationCreateBody as d, fieldListResponseSchema as dn, sectionsListQuery as dt, commentStatusBody as en, countResponseSchema as er, searchEnableBody as et, bylineTranslationsResponseSchema as f, fieldReorderBody as fn, updateSectionBody as ft, createRedirectBody as g, orphanedTableListResponseSchema as gn, taxonomyDefTranslationsSchema as gt, contentBylineInputSchema as h, orphanRegisterBody as hn, taxonomyDefSchema as ht, bylineFieldReorderBody as i, collectionGetQuery as in, localeCode as ir, searchResultSchema as it, updateWidgetBody as j, mediaUpdateBody as jn, menuTranslationsSchema as jt, createWidgetBody as k, mediaProviderListQuery as kn, menuListItemSchema as kt, bylineListResponseSchema as l, createCollectionBody as ln, successEnvelope as lr, sectionListResponseSchema as lt, bylinesListQuery as m, fieldSchema as mn, createTermBody as mt, bylineFieldDefinitionSchema as n, publicCommentListResponseSchema as nn, deleteResponseSchema as nr, searchRebuildBody as nt, bylineFieldUsageResponseSchema as o, collectionResponseSchema as on, offsetPaginationQuery as or, settingsUpdateBody as ot, bylineUpdateBody as p, fieldResponseSchema as pn, createTaxonomyDefBody as pt, setupBody as q, contentTranslationsResponseSchema as qn, signupRequestBody as qt, bylineFieldListResponseSchema as r, publicCommentSchema as rn, httpUrl as rr, searchResponseSchema as rt, bylineCreateBody as s, collectionSchema as sn, roleLevel as sr, siteSettingsSchema as st, bylineFieldCreateBody as t, createCommentBody as tn, cursorPaginationQuery as tr, searchQuery as tt, bylineSummarySchema as u, createFieldBody as un, sectionSchema as ut, notFoundListQuery as v, schemaExportQuery as vn, termGetResponseSchema as vt, redirectListResponseSchema as w, mediaConfirmResponseSchema as wn, updateTermBody as wt, notFoundSummaryQuery as x, DEFAULT_MAX_UPLOAD_SIZE as xn, termSchema as xt, notFoundListResponseSchema as y, updateCollectionBody as yn, termListResponseSchema as yt, userListResponseSchema as z, contentPreviewUrlBody as zn, inviteRegisterOptionsBody as zt };
2144
- //# sourceMappingURL=byline-fields-BNy7Ng1U.d.mts.map
2144
+ //# sourceMappingURL=byline-fields-CR5hGLMw.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"byline-fields-BNy7Ng1U.d.mts","names":[],"sources":["../src/api/schemas/common.ts","../src/api/schemas/content.ts","../src/api/schemas/media.ts","../src/api/schemas/schema.ts","../src/api/schemas/comments.ts","../src/api/schemas/auth.ts","../src/api/schemas/menus.ts","../src/api/schemas/taxonomies.ts","../src/api/schemas/sections.ts","../src/api/schemas/settings.ts","../src/api/schemas/search.ts","../src/api/schemas/import.ts","../src/api/schemas/setup.ts","../src/api/schemas/users.ts","../src/api/schemas/widgets.ts","../src/api/schemas/redirects.ts","../src/api/schemas/bylines.ts","../src/api/schemas/byline-fields.ts"],"mappings":"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"}
1
+ {"version":3,"file":"byline-fields-CR5hGLMw.d.mts","names":[],"sources":["../src/api/schemas/common.ts","../src/api/schemas/content.ts","../src/api/schemas/media.ts","../src/api/schemas/schema.ts","../src/api/schemas/comments.ts","../src/api/schemas/auth.ts","../src/api/schemas/menus.ts","../src/api/schemas/taxonomies.ts","../src/api/schemas/sections.ts","../src/api/schemas/settings.ts","../src/api/schemas/search.ts","../src/api/schemas/import.ts","../src/api/schemas/setup.ts","../src/api/schemas/users.ts","../src/api/schemas/widgets.ts","../src/api/schemas/redirects.ts","../src/api/schemas/bylines.ts","../src/api/schemas/byline-fields.ts"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { n as getI18nConfig } from "./config-CVssduLe.mjs";
2
- import { t as EmDashValidationError } from "./types-SF1DwGf2.mjs";
3
- import { t as BylineRepository } from "./byline-BrIVWLm-.mjs";
2
+ import { t as EmDashValidationError } from "./types-CfyYQ7eY.mjs";
3
+ import { t as BylineRepository } from "./byline-CAhk4FrG.mjs";
4
4
 
5
5
  //#region src/api/handlers/bylines.ts
6
6
  const norm = (v) => v ?? null;
@@ -201,4 +201,4 @@ async function handleBylineUpdate(db, id, input) {
201
201
 
202
202
  //#endregion
203
203
  export { handleBylineTranslations as n, handleBylineUpdate as r, handleBylineCreate as t };
204
- //# sourceMappingURL=bylines-sqExMElV.mjs.map
204
+ //# sourceMappingURL=bylines-CbrD7STW.mjs.map