emdash 0.16.1 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (566) hide show
  1. package/dist/{adapters-C4yd_UJR.d.mts → adapters-C5AWLJSD.d.mts} +1 -1
  2. package/dist/{adapters-C4yd_UJR.d.mts.map → adapters-C5AWLJSD.d.mts.map} +1 -1
  3. package/dist/{allowed-origins-D0fFk9a6.mjs → allowed-origins-CyYLEJkp.mjs} +2 -2
  4. package/dist/{allowed-origins-D0fFk9a6.mjs.map → allowed-origins-CyYLEJkp.mjs.map} +1 -1
  5. package/dist/api/route-utils.d.mts +3 -3
  6. package/dist/api/route-utils.mjs +16 -16
  7. package/dist/api/schemas/index.d.mts +2 -2
  8. package/dist/api/schemas/index.mjs +3 -3
  9. package/dist/{api-BNKqxyFX.mjs → api-Dmz40c2V.mjs} +44 -22
  10. package/dist/api-Dmz40c2V.mjs.map +1 -0
  11. package/dist/{api-tokens-ucpcNXDt.mjs → api-tokens-VrXNiNvV.mjs} +2 -2
  12. package/dist/{api-tokens-ucpcNXDt.mjs.map → api-tokens-VrXNiNvV.mjs.map} +1 -1
  13. package/dist/{apply-BOPaD-s9.mjs → apply-CuuZG6op.mjs} +93 -31
  14. package/dist/apply-CuuZG6op.mjs.map +1 -0
  15. package/dist/astro/index.d.mts +10 -10
  16. package/dist/astro/index.mjs +28 -3
  17. package/dist/astro/index.mjs.map +1 -1
  18. package/dist/astro/middleware/auth.d.mts +9 -9
  19. package/dist/astro/middleware/auth.mjs +6 -6
  20. package/dist/astro/middleware/redirect.d.mts.map +1 -1
  21. package/dist/astro/middleware/redirect.mjs +9 -5
  22. package/dist/astro/middleware/redirect.mjs.map +1 -1
  23. package/dist/astro/middleware/request-context.mjs +2 -2
  24. package/dist/astro/middleware/setup.mjs +1 -1
  25. package/dist/astro/middleware.mjs +66 -65
  26. package/dist/astro/middleware.mjs.map +1 -1
  27. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +5 -5
  28. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +5 -5
  29. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
  30. package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
  31. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.d.mts +8 -0
  32. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.d.mts.map +1 -0
  33. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +23 -0
  34. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs.map +1 -0
  35. package/dist/astro/routes/api/admin/byline-fields/_slug_.d.mts +10 -0
  36. package/dist/astro/routes/api/admin/byline-fields/_slug_.d.mts.map +1 -0
  37. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +55 -0
  38. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs.map +1 -0
  39. package/dist/astro/routes/api/admin/byline-fields/index.d.mts +9 -0
  40. package/dist/astro/routes/api/admin/byline-fields/index.d.mts.map +1 -0
  41. package/dist/astro/routes/api/admin/byline-fields/index.mjs +43 -0
  42. package/dist/astro/routes/api/admin/byline-fields/index.mjs.map +1 -0
  43. package/dist/astro/routes/api/admin/byline-fields/reorder.d.mts +8 -0
  44. package/dist/astro/routes/api/admin/byline-fields/reorder.d.mts.map +1 -0
  45. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +27 -0
  46. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs.map +1 -0
  47. package/dist/astro/routes/api/admin/bylines/_id_/index.d.mts.map +1 -1
  48. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +27 -28
  49. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs.map +1 -1
  50. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +13 -12
  51. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs.map +1 -1
  52. package/dist/astro/routes/api/admin/bylines/index.mjs +15 -13
  53. package/dist/astro/routes/api/admin/bylines/index.mjs.map +1 -1
  54. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +10 -10
  55. package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
  56. package/dist/astro/routes/api/admin/comments/bulk.mjs +8 -8
  57. package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
  58. package/dist/astro/routes/api/admin/comments/index.mjs +8 -8
  59. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
  60. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  61. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +4 -4
  62. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
  63. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +35 -34
  64. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  65. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +35 -34
  66. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  67. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +34 -33
  68. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  69. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +34 -33
  70. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  71. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +34 -33
  72. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  73. package/dist/astro/routes/api/admin/plugins/index.mjs +34 -33
  74. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  75. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
  76. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +34 -33
  77. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  78. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +34 -33
  79. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  80. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +34 -33
  81. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  82. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +34 -33
  83. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  84. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +35 -34
  85. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  86. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +34 -33
  87. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  88. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +35 -34
  89. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  90. package/dist/astro/routes/api/admin/plugins/updates.mjs +34 -33
  91. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  92. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +34 -33
  93. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  94. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
  95. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +34 -33
  96. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  97. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
  98. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
  99. package/dist/astro/routes/api/admin/users/_id_/index.mjs +5 -5
  100. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +3 -3
  101. package/dist/astro/routes/api/admin/users/index.mjs +5 -5
  102. package/dist/astro/routes/api/auth/dev-bypass.mjs +5 -5
  103. package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
  104. package/dist/astro/routes/api/auth/invite/complete.mjs +9 -9
  105. package/dist/astro/routes/api/auth/invite/index.mjs +6 -6
  106. package/dist/astro/routes/api/auth/invite/register-options.mjs +8 -8
  107. package/dist/astro/routes/api/auth/logout.mjs +3 -3
  108. package/dist/astro/routes/api/auth/magic-link/send.mjs +8 -8
  109. package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
  110. package/dist/astro/routes/api/auth/me.d.mts.map +1 -1
  111. package/dist/astro/routes/api/auth/me.mjs +18 -11
  112. package/dist/astro/routes/api/auth/me.mjs.map +1 -1
  113. package/dist/astro/routes/api/auth/mode.mjs +1 -1
  114. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
  115. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  116. package/dist/astro/routes/api/auth/passkey/_id_.mjs +5 -5
  117. package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
  118. package/dist/astro/routes/api/auth/passkey/options.mjs +10 -10
  119. package/dist/astro/routes/api/auth/passkey/register/options.mjs +8 -8
  120. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +9 -9
  121. package/dist/astro/routes/api/auth/passkey/verify.mjs +9 -9
  122. package/dist/astro/routes/api/auth/signup/complete.mjs +9 -9
  123. package/dist/astro/routes/api/auth/signup/request.mjs +8 -8
  124. package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
  125. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +11 -11
  126. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
  127. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
  128. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
  129. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
  130. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +9 -9
  131. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +6 -6
  132. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
  133. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
  134. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +6 -6
  135. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.d.mts.map +1 -1
  136. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +18 -13
  137. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs.map +1 -1
  138. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
  139. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
  140. package/dist/astro/routes/api/content/_collection_/_id_.d.mts.map +1 -1
  141. package/dist/astro/routes/api/content/_collection_/_id_.mjs +9 -7
  142. package/dist/astro/routes/api/content/_collection_/_id_.mjs.map +1 -1
  143. package/dist/astro/routes/api/content/_collection_/index.mjs +6 -6
  144. package/dist/astro/routes/api/content/_collection_/trash.mjs +6 -6
  145. package/dist/astro/routes/api/dashboard.mjs +7 -7
  146. package/dist/astro/routes/api/dev/emails.mjs +3 -3
  147. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  148. package/dist/astro/routes/api/import/probe.mjs +10 -10
  149. package/dist/astro/routes/api/import/wordpress/analyze.mjs +4 -4
  150. package/dist/astro/routes/api/import/wordpress/execute.d.mts +9 -9
  151. package/dist/astro/routes/api/import/wordpress/execute.mjs +11 -10
  152. package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
  153. package/dist/astro/routes/api/import/wordpress/media.mjs +8 -8
  154. package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
  155. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +8 -8
  156. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  157. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +10 -10
  158. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +1 -1
  159. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +13 -11
  160. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
  161. package/dist/astro/routes/api/manifest.mjs +4 -4
  162. package/dist/astro/routes/api/mcp.mjs +34 -30
  163. package/dist/astro/routes/api/mcp.mjs.map +1 -1
  164. package/dist/astro/routes/api/media/_id_/confirm.mjs +6 -6
  165. package/dist/astro/routes/api/media/_id_.mjs +6 -6
  166. package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
  167. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
  168. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
  169. package/dist/astro/routes/api/media/providers/index.mjs +3 -3
  170. package/dist/astro/routes/api/media/upload-url.mjs +8 -8
  171. package/dist/astro/routes/api/media.d.mts.map +1 -1
  172. package/dist/astro/routes/api/media.mjs +13 -12
  173. package/dist/astro/routes/api/media.mjs.map +1 -1
  174. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +7 -7
  175. package/dist/astro/routes/api/menus/_name_/items.mjs +7 -7
  176. package/dist/astro/routes/api/menus/_name_/reorder.mjs +7 -7
  177. package/dist/astro/routes/api/menus/_name_/translations.mjs +7 -7
  178. package/dist/astro/routes/api/menus/_name_.mjs +7 -7
  179. package/dist/astro/routes/api/menus/index.mjs +7 -7
  180. package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
  181. package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
  182. package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
  183. package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
  184. package/dist/astro/routes/api/oauth/register.mjs +3 -3
  185. package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
  186. package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
  187. package/dist/astro/routes/api/oauth/token.mjs +6 -6
  188. package/dist/astro/routes/api/openapi.json.mjs +10 -7
  189. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  190. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +4 -4
  191. package/dist/astro/routes/api/redirects/404s/index.mjs +8 -8
  192. package/dist/astro/routes/api/redirects/404s/summary.mjs +8 -8
  193. package/dist/astro/routes/api/redirects/_id_.mjs +9 -9
  194. package/dist/astro/routes/api/redirects/index.mjs +9 -9
  195. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
  196. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
  197. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +34 -33
  198. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  199. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +34 -33
  200. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  201. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +34 -33
  202. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  203. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +34 -33
  204. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  205. package/dist/astro/routes/api/schema/collections/index.mjs +34 -33
  206. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  207. package/dist/astro/routes/api/schema/index.mjs +6 -6
  208. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +34 -33
  209. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  210. package/dist/astro/routes/api/schema/orphans/index.mjs +34 -33
  211. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  212. package/dist/astro/routes/api/search/enable.mjs +9 -9
  213. package/dist/astro/routes/api/search/index.mjs +8 -8
  214. package/dist/astro/routes/api/search/rebuild.mjs +9 -9
  215. package/dist/astro/routes/api/search/stats.mjs +6 -6
  216. package/dist/astro/routes/api/search/suggest.mjs +8 -8
  217. package/dist/astro/routes/api/sections/_slug_.mjs +8 -8
  218. package/dist/astro/routes/api/sections/index.mjs +8 -8
  219. package/dist/astro/routes/api/settings/email.mjs +4 -4
  220. package/dist/astro/routes/api/settings.mjs +11 -11
  221. package/dist/astro/routes/api/setup/admin-verify.mjs +10 -10
  222. package/dist/astro/routes/api/setup/admin.mjs +9 -9
  223. package/dist/astro/routes/api/setup/dev-bypass.mjs +24 -23
  224. package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
  225. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  226. package/dist/astro/routes/api/setup/index.mjs +24 -23
  227. package/dist/astro/routes/api/setup/index.mjs.map +1 -1
  228. package/dist/astro/routes/api/setup/status.mjs +4 -4
  229. package/dist/astro/routes/api/snapshot.mjs +5 -5
  230. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +12 -12
  231. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +12 -12
  232. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +12 -12
  233. package/dist/astro/routes/api/taxonomies/index.mjs +12 -12
  234. package/dist/astro/routes/api/themes/preview.mjs +5 -5
  235. package/dist/astro/routes/api/typegen.mjs +5 -5
  236. package/dist/astro/routes/api/well-known/auth.mjs +1 -1
  237. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  238. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  239. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +6 -6
  240. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +8 -8
  241. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +8 -8
  242. package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
  243. package/dist/astro/routes/api/widget-areas/index.mjs +8 -8
  244. package/dist/astro/routes/api/widget-components.mjs +3 -3
  245. package/dist/astro/routes/robots.txt.mjs +6 -6
  246. package/dist/astro/routes/sitemap-_collection_.xml.mjs +8 -8
  247. package/dist/astro/routes/sitemap.xml.mjs +7 -7
  248. package/dist/astro/types.d.mts +13 -12
  249. package/dist/astro/types.d.mts.map +1 -1
  250. package/dist/auth/providers/github.d.mts +1 -1
  251. package/dist/auth/providers/google.d.mts +1 -1
  252. package/dist/{authorize-Bn4S4DUT.mjs → authorize-_wWM_44T.mjs} +2 -2
  253. package/dist/{authorize-Bn4S4DUT.mjs.map → authorize-_wWM_44T.mjs.map} +1 -1
  254. package/dist/byline-BrIVWLm-.mjs +925 -0
  255. package/dist/byline-BrIVWLm-.mjs.map +1 -0
  256. package/dist/{bylines-DWLnr6-k.d.mts → byline-fields-BNy7Ng1U.d.mts} +151 -23
  257. package/dist/byline-fields-BNy7Ng1U.d.mts.map +1 -0
  258. package/dist/byline-fields-DC3Wkk-U.mjs +123 -0
  259. package/dist/byline-fields-DC3Wkk-U.mjs.map +1 -0
  260. package/dist/byline-fields-Dr-xcb6S.mjs +238 -0
  261. package/dist/byline-fields-Dr-xcb6S.mjs.map +1 -0
  262. package/dist/byline-registry-CxK5g559.mjs +406 -0
  263. package/dist/byline-registry-CxK5g559.mjs.map +1 -0
  264. package/dist/{bylines-n6nykUyI.mjs → bylines-C_POWmGT.mjs} +25 -11
  265. package/dist/{bylines-n6nykUyI.mjs.map → bylines-C_POWmGT.mjs.map} +1 -1
  266. package/dist/bylines-sqExMElV.mjs +204 -0
  267. package/dist/bylines-sqExMElV.mjs.map +1 -0
  268. package/dist/{cache-BcI1yUjR.mjs → cache-wsDkA8ru.mjs} +2 -2
  269. package/dist/{cache-BcI1yUjR.mjs.map → cache-wsDkA8ru.mjs.map} +1 -1
  270. package/dist/{challenge-store-Dng1SxKT.mjs → challenge-store-DGwuCc4R.mjs} +1 -1
  271. package/dist/{challenge-store-Dng1SxKT.mjs.map → challenge-store-DGwuCc4R.mjs.map} +1 -1
  272. package/dist/{chunks-cYG4SnIP.mjs → chunks-BAYkM-CF.mjs} +2 -2
  273. package/dist/{chunks-cYG4SnIP.mjs.map → chunks-BAYkM-CF.mjs.map} +1 -1
  274. package/dist/cli/index.mjs +140 -32
  275. package/dist/cli/index.mjs.map +1 -1
  276. package/dist/client/cf-access.d.mts +1 -1
  277. package/dist/client/index.d.mts +2 -1
  278. package/dist/client/index.d.mts.map +1 -1
  279. package/dist/client/index.mjs +4 -2
  280. package/dist/client/index.mjs.map +1 -1
  281. package/dist/{comment-C76G-9tz.mjs → comment-Cd29aktf.mjs} +2 -2
  282. package/dist/{comment-C76G-9tz.mjs.map → comment-Cd29aktf.mjs.map} +1 -1
  283. package/dist/{comments-CCxFFGY1.mjs → comments-B7ufhkxN.mjs} +3 -3
  284. package/dist/{comments-CCxFFGY1.mjs.map → comments-B7ufhkxN.mjs.map} +1 -1
  285. package/dist/{components-Dx3DM0gg.mjs → components-CTfpu3PZ.mjs} +1 -1
  286. package/dist/{components-Dx3DM0gg.mjs.map → components-CTfpu3PZ.mjs.map} +1 -1
  287. package/dist/{content-8voQNTXX.mjs → content-BbqKo3Kc.mjs} +22 -3
  288. package/dist/content-BbqKo3Kc.mjs.map +1 -0
  289. package/dist/{context-B7qiYrz2.mjs → context-BsF1rhoI.mjs} +9 -9
  290. package/dist/{context-B7qiYrz2.mjs.map → context-BsF1rhoI.mjs.map} +1 -1
  291. package/dist/{cron-Bd3b3iuj.mjs → cron-DZovZUnC.mjs} +1 -1
  292. package/dist/{cron-Bd3b3iuj.mjs.map → cron-DZovZUnC.mjs.map} +1 -1
  293. package/dist/{dashboard-BeaFSPpx.mjs → dashboard-BwIX9r-X.mjs} +4 -4
  294. package/dist/{dashboard-BeaFSPpx.mjs.map → dashboard-BwIX9r-X.mjs.map} +1 -1
  295. package/dist/db/index.d.mts +3 -3
  296. package/dist/db/index.mjs +1 -1
  297. package/dist/db/libsql.d.mts +1 -1
  298. package/dist/db/postgres.d.mts +1 -1
  299. package/dist/db/sqlite.d.mts +1 -1
  300. package/dist/{db-errors-BiYqoX-n.mjs → db-errors-CtzxKBxe.mjs} +1 -1
  301. package/dist/{db-errors-BiYqoX-n.mjs.map → db-errors-CtzxKBxe.mjs.map} +1 -1
  302. package/dist/{default-BvTAYCzx.mjs → default-xLFNSsZ9.mjs} +1 -1
  303. package/dist/{default-BvTAYCzx.mjs.map → default-xLFNSsZ9.mjs.map} +1 -1
  304. package/dist/{device-flow-B9oG8PwP.mjs → device-flow-ptLrVINd.mjs} +4 -4
  305. package/dist/{device-flow-B9oG8PwP.mjs.map → device-flow-ptLrVINd.mjs.map} +1 -1
  306. package/dist/{email-console-CubRll9q.mjs → email-console-DHT2Fbpj.mjs} +1 -1
  307. package/dist/{email-console-CubRll9q.mjs.map → email-console-DHT2Fbpj.mjs.map} +1 -1
  308. package/dist/{error-ChfADBuu.mjs → error-npZWBSb7.mjs} +7 -3
  309. package/dist/error-npZWBSb7.mjs.map +1 -0
  310. package/dist/{escape-Cg6kMELH.mjs → escape-bIyGoW5W.mjs} +1 -1
  311. package/dist/{escape-Cg6kMELH.mjs.map → escape-bIyGoW5W.mjs.map} +1 -1
  312. package/dist/{fts-manager-C_b-4x8u.mjs → fts-manager-DmUAk-kQ.mjs} +2 -2
  313. package/dist/{fts-manager-C_b-4x8u.mjs.map → fts-manager-DmUAk-kQ.mjs.map} +1 -1
  314. package/dist/{hash-DlUxGhQS.mjs → hash-9w3pd3-m.mjs} +1 -1
  315. package/dist/{hash-DlUxGhQS.mjs.map → hash-9w3pd3-m.mjs.map} +1 -1
  316. package/dist/{import-DG80rC_I.mjs → import-Dh8bWmyq.mjs} +3 -3
  317. package/dist/{import-DG80rC_I.mjs.map → import-Dh8bWmyq.mjs.map} +1 -1
  318. package/dist/{index-D_p_jIP1.d.mts → index-CjKdMZ3U.d.mts} +38 -16
  319. package/dist/index-CjKdMZ3U.d.mts.map +1 -0
  320. package/dist/{index-CC42STEm.d.mts → index-D60_SzHG.d.mts} +3 -3
  321. package/dist/{index-CC42STEm.d.mts.map → index-D60_SzHG.d.mts.map} +1 -1
  322. package/dist/index.d.mts +17 -17
  323. package/dist/index.mjs +55 -54
  324. package/dist/{load-CLFRjk9r.mjs → load-DsoLq7ex.mjs} +2 -2
  325. package/dist/{load-CLFRjk9r.mjs.map → load-DsoLq7ex.mjs.map} +1 -1
  326. package/dist/{loader-D-vIJjfY.mjs → loader-CJ6lWO0d.mjs} +75 -19
  327. package/dist/loader-CJ6lWO0d.mjs.map +1 -0
  328. package/dist/{manifest-schema-Czqf0TLu.mjs → manifest-schema-Cj-YrzrF.mjs} +1 -1
  329. package/dist/{manifest-schema-Czqf0TLu.mjs.map → manifest-schema-Cj-YrzrF.mjs.map} +1 -1
  330. package/dist/media/index.d.mts +1 -1
  331. package/dist/media/index.mjs +2 -2
  332. package/dist/media/local-runtime.d.mts +11 -11
  333. package/dist/media/local-runtime.mjs +5 -5
  334. package/dist/{media-allowlist-BNloC69x.mjs → media-allowlist-CMcoYIjQ.mjs} +2 -2
  335. package/dist/{media-allowlist-BNloC69x.mjs.map → media-allowlist-CMcoYIjQ.mjs.map} +1 -1
  336. package/dist/{media-CKQd8AYU.mjs → media-jk_HzzOl.mjs} +7 -2
  337. package/dist/media-jk_HzzOl.mjs.map +1 -0
  338. package/dist/{menus-arUNspyU.mjs → menus-B-5-3aon.mjs} +2 -2
  339. package/dist/{menus-arUNspyU.mjs.map → menus-B-5-3aon.mjs.map} +1 -1
  340. package/dist/{menus-C-nWT5Tu.mjs → menus-CyMO6GBx.mjs} +27 -11
  341. package/dist/menus-CyMO6GBx.mjs.map +1 -0
  342. package/dist/{mime-KV5TqkMN.mjs → mime-CCEzze7W.mjs} +1 -1
  343. package/dist/{mime-KV5TqkMN.mjs.map → mime-CCEzze7W.mjs.map} +1 -1
  344. package/dist/{mode-CaaiebZI.mjs → mode-BjlXswIw.mjs} +1 -1
  345. package/dist/{mode-CaaiebZI.mjs.map → mode-BjlXswIw.mjs.map} +1 -1
  346. package/dist/{normalize-CN5kRSMC.mjs → normalize-DVV8nbrL.mjs} +1 -1
  347. package/dist/{normalize-CN5kRSMC.mjs.map → normalize-DVV8nbrL.mjs.map} +1 -1
  348. package/dist/{oauth-authorization-CTMeVfvj.mjs → oauth-authorization-DvBAL75d.mjs} +4 -4
  349. package/dist/{oauth-authorization-CTMeVfvj.mjs.map → oauth-authorization-DvBAL75d.mjs.map} +1 -1
  350. package/dist/{oauth-clients-eJCbkVSG.mjs → oauth-clients-8mPDStMv.mjs} +1 -1
  351. package/dist/{oauth-clients-eJCbkVSG.mjs.map → oauth-clients-8mPDStMv.mjs.map} +1 -1
  352. package/dist/{oauth-state-store-vOSdOeGe.mjs → oauth-state-store-BJ7YtrfD.mjs} +1 -1
  353. package/dist/{oauth-state-store-vOSdOeGe.mjs.map → oauth-state-store-BJ7YtrfD.mjs.map} +1 -1
  354. package/dist/{oauth-user-lookup-3JwsVw6N.mjs → oauth-user-lookup-BdDSDvjF.mjs} +1 -1
  355. package/dist/{oauth-user-lookup-3JwsVw6N.mjs.map → oauth-user-lookup-BdDSDvjF.mjs.map} +1 -1
  356. package/dist/{options-DhV-gwJb.d.mts → options-tb7DJROi.d.mts} +3 -3
  357. package/dist/{options-DhV-gwJb.d.mts.map → options-tb7DJROi.d.mts.map} +1 -1
  358. package/dist/page/index.d.mts +2 -2
  359. package/dist/{parse-DHbXfvxO.mjs → parse-4zO5Y2DL.mjs} +2 -2
  360. package/dist/{parse-DHbXfvxO.mjs.map → parse-4zO5Y2DL.mjs.map} +1 -1
  361. package/dist/{passkey-config-BloQOT3y.mjs → passkey-config-BDVM86Tj.mjs} +1 -1
  362. package/dist/{passkey-config-BloQOT3y.mjs.map → passkey-config-BDVM86Tj.mjs.map} +1 -1
  363. package/dist/{placeholder-KCkkCtgQ.d.mts → placeholder-B9lUUEmj.d.mts} +1 -1
  364. package/dist/{placeholder-KCkkCtgQ.d.mts.map → placeholder-B9lUUEmj.d.mts.map} +1 -1
  365. package/dist/{placeholder-LqmHqvBw.mjs → placeholder-BZxr8W1j.mjs} +1 -1
  366. package/dist/{placeholder-LqmHqvBw.mjs.map → placeholder-BZxr8W1j.mjs.map} +1 -1
  367. package/dist/plugin-types.d.mts +1 -1
  368. package/dist/plugin-utils.d.mts +9 -9
  369. package/dist/plugins/adapt-sandbox-entry.d.mts +9 -9
  370. package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
  371. package/dist/{preview-D4z0WONU.mjs → preview-BfuRkVKW.mjs} +2 -2
  372. package/dist/{preview-D4z0WONU.mjs.map → preview-BfuRkVKW.mjs.map} +1 -1
  373. package/dist/{public-url-CUWWFME2.mjs → public-url-egRHCy1m.mjs} +1 -1
  374. package/dist/{public-url-CUWWFME2.mjs.map → public-url-egRHCy1m.mjs.map} +1 -1
  375. package/dist/{query-7m6-l0f_.mjs → query-Bt52mHXp.mjs} +19 -18
  376. package/dist/query-Bt52mHXp.mjs.map +1 -0
  377. package/dist/{rate-limit-D8RAXN8b.mjs → rate-limit-D6VQqBk_.mjs} +2 -2
  378. package/dist/{rate-limit-D8RAXN8b.mjs.map → rate-limit-D6VQqBk_.mjs.map} +1 -1
  379. package/dist/{redirect-CjfDGrTd.mjs → redirect-BZUJltlj.mjs} +2 -2
  380. package/dist/{redirect-CjfDGrTd.mjs.map → redirect-BZUJltlj.mjs.map} +1 -1
  381. package/dist/{redirect-BINiRYq4.mjs → redirect-Cw3JTlmj.mjs} +1 -1
  382. package/dist/{redirect-BINiRYq4.mjs.map → redirect-Cw3JTlmj.mjs.map} +1 -1
  383. package/dist/{redirects-COMLwsV5.mjs → redirects-C0L9JUk4.mjs} +19 -6
  384. package/dist/redirects-C0L9JUk4.mjs.map +1 -0
  385. package/dist/{redirects-CowoEHdE.mjs → redirects-DnYuqsEf.mjs} +3 -3
  386. package/dist/{redirects-CowoEHdE.mjs.map → redirects-DnYuqsEf.mjs.map} +1 -1
  387. package/dist/{registry-Cyp-dx6J.mjs → registry-Dn6gsx3L.mjs} +13 -5
  388. package/dist/{registry-Cyp-dx6J.mjs.map → registry-Dn6gsx3L.mjs.map} +1 -1
  389. package/dist/{request-cache-dzCt8TZB.mjs → request-cache-BYMs-BGX.mjs} +23 -2
  390. package/dist/{request-cache-dzCt8TZB.mjs.map → request-cache-BYMs-BGX.mjs.map} +1 -1
  391. package/dist/{request-meta-C_Cjii-T.mjs → request-meta-7ByVLxB-.mjs} +2 -2
  392. package/dist/{request-meta-C_Cjii-T.mjs.map → request-meta-7ByVLxB-.mjs.map} +1 -1
  393. package/dist/{resolve-D6sM-SgF.mjs → resolve-BqYMVG0D.mjs} +1 -1
  394. package/dist/{resolve-D6sM-SgF.mjs.map → resolve-BqYMVG0D.mjs.map} +1 -1
  395. package/dist/{runner-DSQBurMS.d.mts → runner-DM1yR5qd.d.mts} +2 -2
  396. package/dist/{runner-DSQBurMS.d.mts.map → runner-DM1yR5qd.d.mts.map} +1 -1
  397. package/dist/{runner-Drnvs96u.mjs → runner-eAgyIkeg.mjs} +284 -158
  398. package/dist/runner-eAgyIkeg.mjs.map +1 -0
  399. package/dist/runtime.d.mts +10 -10
  400. package/dist/runtime.mjs +2 -2
  401. package/dist/{schema-CI9mYPX3.mjs → schema--mYZX4D7.mjs} +5 -5
  402. package/dist/{schema-CI9mYPX3.mjs.map → schema--mYZX4D7.mjs.map} +1 -1
  403. package/dist/{search-DKz_mGBP.mjs → search-C6U_NvZI.mjs} +4 -4
  404. package/dist/{search-DKz_mGBP.mjs.map → search-C6U_NvZI.mjs.map} +1 -1
  405. package/dist/{secrets-rPdhEBkD.mjs → secrets-YYbTgB1w.mjs} +1 -1
  406. package/dist/{secrets-rPdhEBkD.mjs.map → secrets-YYbTgB1w.mjs.map} +1 -1
  407. package/dist/{sections-DBbCDIAT.mjs → sections-Ba-rJLKb.mjs} +3 -3
  408. package/dist/{sections-DBbCDIAT.mjs.map → sections-Ba-rJLKb.mjs.map} +1 -1
  409. package/dist/seed/index.d.mts +2 -2
  410. package/dist/seed/index.mjs +18 -17
  411. package/dist/seo/index.d.mts +1 -1
  412. package/dist/{seo-BGCyDlkb.mjs → seo-BTzb5ksq.mjs} +2 -2
  413. package/dist/{seo-BGCyDlkb.mjs.map → seo-BTzb5ksq.mjs.map} +1 -1
  414. package/dist/{seo-Dq707mNQ.mjs → seo-DfjLvu8i.mjs} +1 -1
  415. package/dist/{seo-Dq707mNQ.mjs.map → seo-DfjLvu8i.mjs.map} +1 -1
  416. package/dist/{service-B0H7U1Y9.mjs → service-Cn-kIfZn.mjs} +3 -3
  417. package/dist/{service-B0H7U1Y9.mjs.map → service-Cn-kIfZn.mjs.map} +1 -1
  418. package/dist/{settings-DfwNyQkf.mjs → settings-C65OSm41.mjs} +3 -3
  419. package/dist/{settings-DfwNyQkf.mjs.map → settings-C65OSm41.mjs.map} +1 -1
  420. package/dist/{settings-BSXRtTzk.mjs → settings-ChlQbwU0.mjs} +4 -4
  421. package/dist/{settings-BSXRtTzk.mjs.map → settings-ChlQbwU0.mjs.map} +1 -1
  422. package/dist/{setup-complete-MzzN9u0b.mjs → setup-complete-VoEZfasi.mjs} +1 -1
  423. package/dist/{setup-complete-MzzN9u0b.mjs.map → setup-complete-VoEZfasi.mjs.map} +1 -1
  424. package/dist/{setup-nonce-DXuriHsg.mjs → setup-nonce-Bm0uKqmf.mjs} +1 -1
  425. package/dist/{setup-nonce-DXuriHsg.mjs.map → setup-nonce-Bm0uKqmf.mjs.map} +1 -1
  426. package/dist/{site-url-xkhw1tcz.mjs → site-url-Cm8-sJy7.mjs} +1 -1
  427. package/dist/{site-url-xkhw1tcz.mjs.map → site-url-Cm8-sJy7.mjs.map} +1 -1
  428. package/dist/{ssrf-MZ-zrG6-.mjs → ssrf-BsVGIE0Z.mjs} +1 -1
  429. package/dist/{ssrf-MZ-zrG6-.mjs.map → ssrf-BsVGIE0Z.mjs.map} +1 -1
  430. package/dist/storage/local.d.mts +1 -1
  431. package/dist/storage/local.mjs +1 -1
  432. package/dist/storage/s3.d.mts +1 -1
  433. package/dist/storage/s3.mjs +1 -1
  434. package/dist/{taxonomies-CcvrMLbR.mjs → taxonomies-ByLlXrv5.mjs} +8 -8
  435. package/dist/{taxonomies-CcvrMLbR.mjs.map → taxonomies-ByLlXrv5.mjs.map} +1 -1
  436. package/dist/{taxonomies-4vx0nmMr.mjs → taxonomies-CbO6v7EE.mjs} +4 -4
  437. package/dist/{taxonomies-4vx0nmMr.mjs.map → taxonomies-CbO6v7EE.mjs.map} +1 -1
  438. package/dist/{taxonomy-zqGQUqgu.mjs → taxonomy-BBK-UAEo.mjs} +3 -3
  439. package/dist/{taxonomy-zqGQUqgu.mjs.map → taxonomy-BBK-UAEo.mjs.map} +1 -1
  440. package/dist/{tokens-N8otWMmj.mjs → tokens-Bx2afeT-.mjs} +1 -1
  441. package/dist/{tokens-N8otWMmj.mjs.map → tokens-Bx2afeT-.mjs.map} +1 -1
  442. package/dist/{transport-B6CHddbu.mjs → transport--Ck3RBin.mjs} +1 -1
  443. package/dist/{transport-B6CHddbu.mjs.map → transport--Ck3RBin.mjs.map} +1 -1
  444. package/dist/{transport-C2MGqtL6.d.mts → transport-OnMNbsIA.d.mts} +1 -1
  445. package/dist/{transport-C2MGqtL6.d.mts.map → transport-OnMNbsIA.d.mts.map} +1 -1
  446. package/dist/{trusted-proxy-97pajC2f.mjs → trusted-proxy-B4AfnoAp.mjs} +1 -1
  447. package/dist/{trusted-proxy-97pajC2f.mjs.map → trusted-proxy-B4AfnoAp.mjs.map} +1 -1
  448. package/dist/types-D8bhH891.mjs +125 -0
  449. package/dist/{types-DSZl1Dsv.mjs.map → types-D8bhH891.mjs.map} +1 -1
  450. package/dist/{types-DGHWRQgr.d.mts → types-DMwSpvcw.d.mts} +2 -2
  451. package/dist/{types-DGHWRQgr.d.mts.map → types-DMwSpvcw.d.mts.map} +1 -1
  452. package/dist/{types-bYmRn_Uy.d.mts → types-DWnN7weG.d.mts} +1 -1
  453. package/dist/{types-bYmRn_Uy.d.mts.map → types-DWnN7weG.d.mts.map} +1 -1
  454. package/dist/{types-Dgo6y-Ut.d.mts → types-DX6v9KzJ.d.mts} +1 -1
  455. package/dist/{types-Dgo6y-Ut.d.mts.map → types-DX6v9KzJ.d.mts.map} +1 -1
  456. package/dist/{types-DaqNzqVt.d.mts → types-DawhLFwy.d.mts} +35 -1
  457. package/dist/{types-DaqNzqVt.d.mts.map → types-DawhLFwy.d.mts.map} +1 -1
  458. package/dist/{types-CpUuGcd5.d.mts → types-DbCWhHet.d.mts} +8 -2
  459. package/dist/{types-CpUuGcd5.d.mts.map → types-DbCWhHet.d.mts.map} +1 -1
  460. package/dist/{types-Cd9UCu3t.mjs → types-DpFmlNyB.mjs} +1 -1
  461. package/dist/{types-Cd9UCu3t.mjs.map → types-DpFmlNyB.mjs.map} +1 -1
  462. package/dist/{types-D599-ruj.d.mts → types-Qa7-HJJC.d.mts} +1 -1
  463. package/dist/{types-D599-ruj.d.mts.map → types-Qa7-HJJC.d.mts.map} +1 -1
  464. package/dist/{types-B0bmgwMG.mjs → types-SF1DwGf2.mjs} +2 -2
  465. package/dist/types-SF1DwGf2.mjs.map +1 -0
  466. package/dist/{types-DaYDYW6g.d.mts → types-i8_uzhMD.d.mts} +40 -2
  467. package/dist/types-i8_uzhMD.d.mts.map +1 -0
  468. package/dist/{types-CkDSF81F.d.mts → types-kwqCOUxj.d.mts} +1 -1
  469. package/dist/{types-CkDSF81F.d.mts.map → types-kwqCOUxj.d.mts.map} +1 -1
  470. package/dist/{user-hUSOaIJy.mjs → user-X4rtyO4Y.mjs} +2 -2
  471. package/dist/{user-hUSOaIJy.mjs.map → user-X4rtyO4Y.mjs.map} +1 -1
  472. package/dist/{utils-C3wTAP-P.mjs → utils-C4Ih4DML.mjs} +1 -1
  473. package/dist/{utils-C3wTAP-P.mjs.map → utils-C4Ih4DML.mjs.map} +1 -1
  474. package/dist/{validate-IGltez8n.mjs → validate-DactmcJG.mjs} +23 -3
  475. package/dist/validate-DactmcJG.mjs.map +1 -0
  476. package/dist/{validate-DQtHw9NT.d.mts → validate-Dy6nkNls.d.mts} +25 -5
  477. package/dist/{validate-DQtHw9NT.d.mts.map → validate-Dy6nkNls.d.mts.map} +1 -1
  478. package/dist/{validation-Bmymau7y.mjs → validation-BYA4i85b.mjs} +6 -6
  479. package/dist/{validation-Bmymau7y.mjs.map → validation-BYA4i85b.mjs.map} +1 -1
  480. package/dist/version-CWbvq9LG.mjs +7 -0
  481. package/dist/{version-ITD3PlQd.mjs.map → version-CWbvq9LG.mjs.map} +1 -1
  482. package/dist/{widgets-yHQa4c6c.mjs → widgets-DG-1jxnz.mjs} +3 -3
  483. package/dist/{widgets-yHQa4c6c.mjs.map → widgets-DG-1jxnz.mjs.map} +1 -1
  484. package/dist/{zod-generator-B80aap1J.mjs → zod-generator-BNAObjSt.mjs} +3 -3
  485. package/dist/{zod-generator-B80aap1J.mjs.map → zod-generator-BNAObjSt.mjs.map} +1 -1
  486. package/package.json +7 -7
  487. package/src/api/errors.ts +7 -0
  488. package/src/api/handlers/byline-fields.ts +212 -0
  489. package/src/api/handlers/bylines.ts +126 -5
  490. package/src/api/handlers/content.ts +43 -2
  491. package/src/api/handlers/media.ts +2 -0
  492. package/src/api/openapi/document.ts +3 -0
  493. package/src/api/schemas/byline-fields.ts +188 -0
  494. package/src/api/schemas/bylines.ts +42 -0
  495. package/src/api/schemas/content.ts +2 -0
  496. package/src/api/schemas/index.ts +1 -0
  497. package/src/api/schemas/media.ts +2 -0
  498. package/src/astro/integration/routes.ts +27 -0
  499. package/src/astro/integration/vite-config.ts +16 -0
  500. package/src/astro/middleware/redirect.ts +5 -1
  501. package/src/astro/routes/api/admin/byline-fields/[slug]/usage.ts +36 -0
  502. package/src/astro/routes/api/admin/byline-fields/[slug].ts +92 -0
  503. package/src/astro/routes/api/admin/byline-fields/index.ts +66 -0
  504. package/src/astro/routes/api/admin/byline-fields/reorder.ts +39 -0
  505. package/src/astro/routes/api/admin/bylines/[id]/index.ts +23 -21
  506. package/src/astro/routes/api/admin/bylines/index.ts +1 -0
  507. package/src/astro/routes/api/auth/me.ts +21 -10
  508. package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +15 -3
  509. package/src/astro/routes/api/content/[collection]/[id].ts +3 -1
  510. package/src/astro/routes/api/media.ts +1 -0
  511. package/src/astro/types.ts +1 -0
  512. package/src/bylines/field-defs-cache.ts +138 -0
  513. package/src/bylines/index.ts +37 -4
  514. package/src/cli/commands/content.ts +4 -2
  515. package/src/cli/commands/export-seed.ts +174 -12
  516. package/src/client/index.ts +4 -1
  517. package/src/components/InlinePortableTextEditor.tsx +69 -0
  518. package/src/content/converters/portable-text-to-prosemirror.ts +7 -0
  519. package/src/content/converters/prosemirror-to-portable-text.ts +16 -0
  520. package/src/content/converters/types.ts +10 -0
  521. package/src/database/migrations/041_content_locale_list_index.ts +47 -0
  522. package/src/database/migrations/042_byline_fields.ts +157 -0
  523. package/src/database/migrations/runner.ts +4 -0
  524. package/src/database/repositories/byline.ts +758 -50
  525. package/src/database/repositories/content.ts +43 -3
  526. package/src/database/repositories/media.ts +14 -0
  527. package/src/database/repositories/types.ts +38 -0
  528. package/src/database/types.ts +44 -0
  529. package/src/emdash-runtime.ts +4 -1
  530. package/src/index.ts +1 -0
  531. package/src/loader.ts +98 -10
  532. package/src/mcp/server.ts +10 -1
  533. package/src/query.ts +7 -7
  534. package/src/request-cache.ts +23 -0
  535. package/src/schema/byline-registry.ts +671 -0
  536. package/src/schema/registry.ts +14 -0
  537. package/src/schema/types.ts +133 -0
  538. package/src/seed/apply.ts +101 -14
  539. package/src/seed/types.ts +21 -0
  540. package/src/seed/validate.ts +39 -0
  541. package/dist/api-BNKqxyFX.mjs.map +0 -1
  542. package/dist/apply-BOPaD-s9.mjs.map +0 -1
  543. package/dist/byline-BDylH_m4.mjs +0 -404
  544. package/dist/byline-BDylH_m4.mjs.map +0 -1
  545. package/dist/bylines-B7TFEvFf.mjs +0 -118
  546. package/dist/bylines-B7TFEvFf.mjs.map +0 -1
  547. package/dist/bylines-DWLnr6-k.d.mts.map +0 -1
  548. package/dist/content-8voQNTXX.mjs.map +0 -1
  549. package/dist/error-ChfADBuu.mjs.map +0 -1
  550. package/dist/index-D_p_jIP1.d.mts.map +0 -1
  551. package/dist/loader-D-vIJjfY.mjs.map +0 -1
  552. package/dist/media-CKQd8AYU.mjs.map +0 -1
  553. package/dist/menus-C-nWT5Tu.mjs.map +0 -1
  554. package/dist/query-7m6-l0f_.mjs.map +0 -1
  555. package/dist/redirects-COMLwsV5.mjs.map +0 -1
  556. package/dist/runner-Drnvs96u.mjs.map +0 -1
  557. package/dist/setup-Cf_TyOv5.mjs +0 -137
  558. package/dist/setup-Cf_TyOv5.mjs.map +0 -1
  559. package/dist/types-B0bmgwMG.mjs.map +0 -1
  560. package/dist/types-DSZl1Dsv.mjs +0 -83
  561. package/dist/types-DaYDYW6g.d.mts.map +0 -1
  562. package/dist/validate-IGltez8n.mjs.map +0 -1
  563. package/dist/version-ITD3PlQd.mjs +0 -7
  564. /package/dist/{api-tokens-iPIHAY8N.mjs → api-tokens-B6VgoE6M.mjs} +0 -0
  565. /package/dist/{ssrf-BIcd-aXW.mjs → ssrf-BvgVcfNQ.mjs} +0 -0
  566. /package/dist/{types-1NNkmTIn.mjs → types-Cj2S6FuC.mjs} +0 -0
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Byline custom-field schema management — single field CRUD.
3
+ *
4
+ * - GET /_emdash/api/admin/byline-fields/{slug} read one definition
5
+ * - PATCH /_emdash/api/admin/byline-fields/{slug} update label / required /
6
+ * translatable / validation /
7
+ * sort order
8
+ * - DELETE /_emdash/api/admin/byline-fields/{slug} drop the definition and all
9
+ * stored values (FK CASCADE +
10
+ * app-level cleanup; see
11
+ * `BylineSchemaRegistry.deleteField`)
12
+ *
13
+ * Thin wrappers around the handler layer; status mapping happens in
14
+ * `unwrapResult`. `slug` and `type` are immutable post-create — see
15
+ * `bylineFieldUpdateBody`. Flipping `translatable` while value rows
16
+ * exist surfaces as a 409 `TRANSLATABLE_LOCKED`.
17
+ *
18
+ * Phase 4 of Discussion #1174.
19
+ */
20
+
21
+ import type { APIRoute } from "astro";
22
+
23
+ import { requirePerm } from "#api/authorize.js";
24
+ import { apiError, requireDb, unwrapResult } from "#api/error.js";
25
+ import {
26
+ handleBylineFieldDelete,
27
+ handleBylineFieldGet,
28
+ handleBylineFieldUpdate,
29
+ } from "#api/handlers/byline-fields.js";
30
+ import { isParseError, parseBody } from "#api/parse.js";
31
+ import { bylineFieldUpdateBody } from "#api/schemas.js";
32
+
33
+ export const prerender = false;
34
+
35
+ // GET requires `schema:read` (Editor+); see sibling `index.ts` GET
36
+ // for rationale.
37
+ export const GET: APIRoute = async ({ params, locals }) => {
38
+ const { emdash, user } = locals;
39
+ const denied = requirePerm(user, "schema:read");
40
+ if (denied) return denied;
41
+
42
+ const dbErr = requireDb(emdash?.db);
43
+ if (dbErr) return dbErr;
44
+
45
+ const slug = params.slug;
46
+ if (!slug) return apiError("MISSING_PARAM", "Field slug is required", 400);
47
+
48
+ const result = await handleBylineFieldGet(emdash.db, slug);
49
+ return unwrapResult(result);
50
+ };
51
+
52
+ export const PATCH: APIRoute = async ({ params, request, locals }) => {
53
+ const { emdash, user } = locals;
54
+ const denied = requirePerm(user, "schema:manage");
55
+ if (denied) return denied;
56
+
57
+ const dbErr = requireDb(emdash?.db);
58
+ if (dbErr) return dbErr;
59
+
60
+ const slug = params.slug;
61
+ if (!slug) return apiError("MISSING_PARAM", "Field slug is required", 400);
62
+
63
+ const body = await parseBody(request, bylineFieldUpdateBody);
64
+ if (isParseError(body)) return body;
65
+
66
+ const result = await handleBylineFieldUpdate(emdash.db, slug, {
67
+ label: body.label,
68
+ required: body.required,
69
+ translatable: body.translatable,
70
+ // `null` clears the stored validation; `undefined` leaves it as-is.
71
+ // The zod schema makes `validation` itself optional, so an absent
72
+ // key reaches the handler as `undefined`.
73
+ validation: body.validation,
74
+ sortOrder: body.sortOrder,
75
+ });
76
+ return unwrapResult(result);
77
+ };
78
+
79
+ export const DELETE: APIRoute = async ({ params, locals }) => {
80
+ const { emdash, user } = locals;
81
+ const denied = requirePerm(user, "schema:manage");
82
+ if (denied) return denied;
83
+
84
+ const dbErr = requireDb(emdash?.db);
85
+ if (dbErr) return dbErr;
86
+
87
+ const slug = params.slug;
88
+ if (!slug) return apiError("MISSING_PARAM", "Field slug is required", 400);
89
+
90
+ const result = await handleBylineFieldDelete(emdash.db, slug);
91
+ return unwrapResult(result);
92
+ };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Byline custom-field schema management — list + create.
3
+ *
4
+ * - GET /_emdash/api/admin/byline-fields list every registered field
5
+ * - POST /_emdash/api/admin/byline-fields create a new field definition
6
+ *
7
+ * Thin wrappers around `handleBylineFieldList` / `handleBylineFieldCreate`
8
+ * in `api/handlers/byline-fields.ts`. Both endpoints require
9
+ * `schema:manage`. Reserved-slug + identifier validation runs at the
10
+ * zod layer in `bylineFieldCreateBody`; the registry repeats the check
11
+ * for defence-in-depth. Domain errors surface as typed `ErrorCode`s and
12
+ * are mapped to HTTP statuses by `unwrapResult` → `mapErrorStatus`.
13
+ *
14
+ * Phase 4 of Discussion #1174.
15
+ */
16
+
17
+ import type { APIRoute } from "astro";
18
+
19
+ import { requirePerm } from "#api/authorize.js";
20
+ import { requireDb, unwrapResult } from "#api/error.js";
21
+ import { handleBylineFieldCreate, handleBylineFieldList } from "#api/handlers/byline-fields.js";
22
+ import { isParseError, parseBody } from "#api/parse.js";
23
+ import { bylineFieldCreateBody } from "#api/schemas.js";
24
+
25
+ export const prerender = false;
26
+
27
+ // GET requires `schema:read` (Editor+), not `schema:manage` — Phase 6
28
+ // of #1174 surfaced the split: editors need to read field definitions
29
+ // to render custom-field inputs in the byline edit form, while only
30
+ // admins manage the registry. The Phase 4 review-round constraint
31
+ // "every endpoint returns 403 for a user without schema:manage" is
32
+ // superseded for the read endpoints; mutations remain admin-only.
33
+ export const GET: APIRoute = async ({ locals }) => {
34
+ const { emdash, user } = locals;
35
+ const denied = requirePerm(user, "schema:read");
36
+ if (denied) return denied;
37
+
38
+ const dbErr = requireDb(emdash?.db);
39
+ if (dbErr) return dbErr;
40
+
41
+ const result = await handleBylineFieldList(emdash.db);
42
+ return unwrapResult(result);
43
+ };
44
+
45
+ export const POST: APIRoute = async ({ request, locals }) => {
46
+ const { emdash, user } = locals;
47
+ const denied = requirePerm(user, "schema:manage");
48
+ if (denied) return denied;
49
+
50
+ const dbErr = requireDb(emdash?.db);
51
+ if (dbErr) return dbErr;
52
+
53
+ const body = await parseBody(request, bylineFieldCreateBody);
54
+ if (isParseError(body)) return body;
55
+
56
+ const result = await handleBylineFieldCreate(emdash.db, {
57
+ slug: body.slug,
58
+ label: body.label,
59
+ type: body.type,
60
+ required: body.required,
61
+ translatable: body.translatable,
62
+ validation: body.validation ?? null,
63
+ sortOrder: body.sortOrder,
64
+ });
65
+ return unwrapResult(result, 201);
66
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Byline field reorder.
3
+ *
4
+ * POST /_emdash/api/admin/byline-fields/reorder
5
+ *
6
+ * Body: `{ slugs: string[] }` — the exact set of currently registered
7
+ * slugs in the desired order. The registry rejects any drift
8
+ * (`REORDER_MISMATCH` → 400). Empty `[]` against an empty registered
9
+ * set is a no-op by registry contract; the zod schema permits it.
10
+ *
11
+ * Thin wrapper around `handleBylineFieldReorder`.
12
+ *
13
+ * Phase 4 of Discussion #1174.
14
+ */
15
+
16
+ import type { APIRoute } from "astro";
17
+
18
+ import { requirePerm } from "#api/authorize.js";
19
+ import { requireDb, unwrapResult } from "#api/error.js";
20
+ import { handleBylineFieldReorder } from "#api/handlers/byline-fields.js";
21
+ import { isParseError, parseBody } from "#api/parse.js";
22
+ import { bylineFieldReorderBody } from "#api/schemas.js";
23
+
24
+ export const prerender = false;
25
+
26
+ export const POST: APIRoute = async ({ request, locals }) => {
27
+ const { emdash, user } = locals;
28
+ const denied = requirePerm(user, "schema:manage");
29
+ if (denied) return denied;
30
+
31
+ const dbErr = requireDb(emdash?.db);
32
+ if (dbErr) return dbErr;
33
+
34
+ const body = await parseBody(request, bylineFieldReorderBody);
35
+ if (isParseError(body)) return body;
36
+
37
+ const result = await handleBylineFieldReorder(emdash.db, body.slugs);
38
+ return unwrapResult(result);
39
+ };
@@ -1,7 +1,8 @@
1
1
  import type { APIRoute } from "astro";
2
2
 
3
3
  import { requirePerm } from "#api/authorize.js";
4
- import { apiError, apiSuccess, handleError } from "#api/error.js";
4
+ import { apiError, apiSuccess, handleError, requireDb, unwrapResult } from "#api/error.js";
5
+ import { handleBylineUpdate } from "#api/handlers/bylines.js";
5
6
  import { isParseError, parseBody } from "#api/parse.js";
6
7
  import { bylineUpdateBody } from "#api/schemas.js";
7
8
  import { invalidateBylineCache } from "#bylines/index.js";
@@ -33,31 +34,32 @@ export const PUT: APIRoute = async ({ params, request, locals }) => {
33
34
  const denied = requirePerm(user, "bylines:manage");
34
35
  if (denied) return denied;
35
36
 
36
- if (!emdash?.db) {
37
- return apiError("NOT_CONFIGURED", "EmDash is not initialized", 500);
38
- }
37
+ const dbErr = requireDb(emdash?.db);
38
+ if (dbErr) return dbErr;
39
39
 
40
40
  const body = await parseBody(request, bylineUpdateBody);
41
41
  if (isParseError(body)) return body;
42
42
 
43
- try {
44
- const repo = new BylineRepository(emdash.db);
45
- const byline = await repo.update(params.id!, {
46
- slug: body.slug,
47
- displayName: body.displayName,
48
- bio: body.bio ?? null,
49
- avatarMediaId: body.avatarMediaId ?? null,
50
- websiteUrl: body.websiteUrl ?? null,
51
- userId: body.userId ?? null,
52
- isGuest: body.isGuest,
53
- });
43
+ const result = await handleBylineUpdate(emdash.db, params.id!, {
44
+ slug: body.slug,
45
+ displayName: body.displayName,
46
+ bio: body.bio ?? null,
47
+ avatarMediaId: body.avatarMediaId ?? null,
48
+ websiteUrl: body.websiteUrl ?? null,
49
+ userId: body.userId ?? null,
50
+ isGuest: body.isGuest,
51
+ // Forward `customFields` only when present so the repo treats an
52
+ // omitted key as "leave existing values untouched". An empty
53
+ // object also no-ops by repo convention — see
54
+ // `BylineRepository.update`. Validation (unknown slug, type
55
+ // mismatch, select-choice) happens inside the repo and surfaces
56
+ // as `EmDashValidationError`, which the handler maps to a 400
57
+ // `VALIDATION_ERROR` for `unwrapResult` / `mapErrorStatus`.
58
+ customFields: body.customFields,
59
+ });
54
60
 
55
- if (!byline) return apiError("NOT_FOUND", "Byline not found", 404);
56
- invalidateBylineCache();
57
- return apiSuccess(byline);
58
- } catch (error) {
59
- return handleError(error, "Failed to update byline", "BYLINE_UPDATE_ERROR");
60
- }
61
+ if (result.success) invalidateBylineCache();
62
+ return unwrapResult(result);
61
63
  };
62
64
 
63
65
  export const DELETE: APIRoute = async ({ params, locals }) => {
@@ -75,6 +75,7 @@ export const POST: APIRoute = async ({ request, locals }) => {
75
75
  isGuest: body.isGuest,
76
76
  locale: body.locale,
77
77
  translationOf: body.translationOf,
78
+ customFields: body.customFields,
78
79
  });
79
80
 
80
81
  if (result.success) invalidateBylineCache();
@@ -9,21 +9,22 @@ import type { APIRoute } from "astro";
9
9
 
10
10
  export const prerender = false;
11
11
 
12
- import { apiError, apiSuccess } from "#api/error.js";
12
+ import { apiError, apiSuccess, handleError } from "#api/error.js";
13
13
  import { isParseError, parseBody } from "#api/parse.js";
14
14
  import { authMeActionBody } from "#api/schemas.js";
15
+ import { UserRepository } from "#db/repositories/user.js";
15
16
 
16
- export const GET: APIRoute = async ({ locals, session }) => {
17
+ export const GET: APIRoute = async ({ locals }) => {
17
18
  const { user } = locals;
18
19
 
19
20
  if (!user) {
20
21
  return apiError("NOT_AUTHENTICATED", "Not authenticated", 401);
21
22
  }
22
23
 
23
- // Check if this is the user's first login (for welcome modal)
24
- // We track this in the session to show the modal only once
25
- const hasSeenWelcome = await session?.get("hasSeenWelcome");
26
- const isFirstLogin = !hasSeenWelcome;
24
+ // Check if this is the user's first login (for welcome modal).
25
+ // The flag is persisted in the user's `data` JSON column so it survives
26
+ // session expiry / rotation.
27
+ const isFirstLogin = !user.data?.welcomeDismissed;
27
28
 
28
29
  // Return safe user info (no sensitive data)
29
30
  return apiSuccess({
@@ -41,19 +42,29 @@ export const GET: APIRoute = async ({ locals, session }) => {
41
42
  *
42
43
  * Mark that the user has seen the welcome modal.
43
44
  */
44
- export const POST: APIRoute = async ({ request, locals, session }) => {
45
- const { user } = locals;
45
+ export const POST: APIRoute = async ({ request, locals }) => {
46
+ const { user, emdash } = locals;
46
47
 
47
48
  if (!user) {
48
49
  return apiError("NOT_AUTHENTICATED", "Not authenticated", 401);
49
50
  }
50
51
 
52
+ if (!emdash) return apiError("NOT_CONFIGURED", "EmDash is not initialized", 500);
53
+
51
54
  const body = await parseBody(request, authMeActionBody);
52
55
  if (isParseError(body)) return body;
53
56
 
54
57
  if (body.action === "dismissWelcome") {
55
- session?.set("hasSeenWelcome", true);
56
- return apiSuccess({ success: true });
58
+ try {
59
+ // Persist in the user's data column so it survives session expiry.
60
+ const userRepo = new UserRepository(emdash.db);
61
+ await userRepo.update(user.id, {
62
+ data: { ...user.data, welcomeDismissed: true },
63
+ });
64
+ return apiSuccess({ success: true });
65
+ } catch (error) {
66
+ return handleError(error, "Failed to dismiss welcome", "WELCOME_DISMISS_ERROR");
67
+ }
57
68
  }
58
69
 
59
70
  return apiError("UNKNOWN_ACTION", "Unknown action", 400);
@@ -11,6 +11,7 @@ import { requirePerm, requireOwnerPerm } from "#api/authorize.js";
11
11
  import { apiError, apiSuccess, handleError, requireDb } from "#api/error.js";
12
12
  import { parseBody, isParseError } from "#api/parse.js";
13
13
  import { contentTermsBody } from "#api/schemas.js";
14
+ import { ContentRepository } from "#db/repositories/content.js";
14
15
  import { TaxonomyRepository } from "#db/repositories/taxonomy.js";
15
16
  import { invalidateTermCache } from "#taxonomies/index.js";
16
17
 
@@ -34,8 +35,16 @@ export const GET: APIRoute = async ({ params, locals }) => {
34
35
  if (dbErr) return dbErr;
35
36
 
36
37
  try {
38
+ // Terms are stored against the per-locale entry row but their
39
+ // translation_group spans every locale. Resolve the entry's own locale
40
+ // server-side (deterministic, not client-spoofable) so only the matching
41
+ // term variant is returned — see issue #1218.
42
+ const entry = await new ContentRepository(emdash.db).findByIdOrSlug(collection, id);
43
+ if (!entry) return apiError("NOT_FOUND", "Content not found", 404);
44
+ const locale = entry.locale ?? undefined;
45
+
37
46
  const repo = new TaxonomyRepository(emdash.db);
38
- const terms = await repo.getTermsForEntry(collection, id, taxonomy);
47
+ const terms = await repo.getTermsForEntry(collection, entry.id, taxonomy, locale);
39
48
 
40
49
  return apiSuccess({
41
50
  terms: terms.map((t) => ({
@@ -101,6 +110,9 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
101
110
  // Resolve the canonical content ID from the handler result.
102
111
  // The URL `id` param may be a slug; we must use the real ID for term storage.
103
112
  const canonicalId = typeof existingItem?.id === "string" ? existingItem.id : id;
113
+ // The entry is per-locale; scope the term read to its locale so only the
114
+ // matching translation variant is returned in the response — see #1218.
115
+ const entryLocale = typeof existingItem?.locale === "string" ? existingItem.locale : undefined;
104
116
 
105
117
  try {
106
118
  const body = await parseBody(request, contentTermsBody);
@@ -131,8 +143,8 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
131
143
  // so hydration on subsequent reads issues a fresh query.
132
144
  invalidateTermCache();
133
145
 
134
- // Get the updated terms using the canonical ID
135
- const terms = await repo.getTermsForEntry(collection, canonicalId, taxonomy);
146
+ // Get the updated terms using the canonical ID, scoped to the entry locale
147
+ const terms = await repo.getTermsForEntry(collection, canonicalId, taxonomy, entryLocale);
136
148
 
137
149
  return apiSuccess({
138
150
  terms: terms.map((t) => ({
@@ -68,6 +68,7 @@ export const PUT: APIRoute = async ({ params, request, locals, cache }) => {
68
68
  const { emdash, user } = locals;
69
69
  const collection = params.collection!;
70
70
  const id = params.id!;
71
+ const locale = new URL(request.url).searchParams.get("locale") || undefined;
71
72
  const body = await parseBody(request, contentUpdateBody);
72
73
  if (isParseError(body)) return body;
73
74
 
@@ -76,7 +77,7 @@ export const PUT: APIRoute = async ({ params, request, locals, cache }) => {
76
77
  }
77
78
 
78
79
  // Fetch item to check ownership
79
- const existing = await emdash.handleContentGet(collection, id);
80
+ const existing = await emdash.handleContentGet(collection, id, locale);
80
81
  if (!existing.success) {
81
82
  return apiError(
82
83
  existing.error?.code ?? "UNKNOWN_ERROR",
@@ -120,6 +121,7 @@ export const PUT: APIRoute = async ({ params, request, locals, cache }) => {
120
121
  // Pass _rev through for optimistic concurrency validation
121
122
  const result = await emdash.handleContentUpdate(collection, resolvedId, {
122
123
  ...updateBody,
124
+ locale,
123
125
  _rev: body._rev,
124
126
  });
125
127
 
@@ -56,6 +56,7 @@ export const GET: APIRoute = async ({ request, locals }) => {
56
56
  cursor: query.cursor,
57
57
  limit: query.limit,
58
58
  mimeType: query.mimeType,
59
+ q: query.q,
59
60
  });
60
61
 
61
62
  if (!result.success) {
@@ -274,6 +274,7 @@ export interface EmDashHandlers {
274
274
  status?: string;
275
275
  authorId?: string | null;
276
276
  bylines?: Array<{ bylineId: string; roleLabel?: string | null }>;
277
+ locale?: string;
277
278
  seo?: {
278
279
  title?: string | null;
279
280
  description?: string | null;
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Byline field-definitions cache
3
+ *
4
+ * Discussion #1174 / Phase 3. Two-tier cache for the byline custom-field
5
+ * registry, mirroring the `settings/index.ts` pattern.
6
+ *
7
+ * **Tier 1 — per-isolate (globalThis).** Field definitions change rarely
8
+ * but are read on every byline hydration (admin pages, content rendering,
9
+ * API responses). Caching at the isolate level drops the SELECT-from-
10
+ * `_emdash_byline_fields` from once-per-hydration to once-per-isolate-
11
+ * after-bump. The cache holds a Promise (not the resolved value) so
12
+ * concurrent cold-isolate readers share the in-flight query.
13
+ *
14
+ * Stored on globalThis under `Symbol.for("emdash:byline-field-defs")` so
15
+ * Vite SSR chunk duplication can't produce two independent caches (same
16
+ * pattern as `request-cache.ts` and `request-context.ts`).
17
+ *
18
+ * **Tier 2 — per-request.** Wraps both the version read and the defs
19
+ * fetch in `requestCached` so a single page render that hits byline
20
+ * hydration multiple times (e.g. list view + individual byline lookups
21
+ * in a sidebar) pays at most one version read and one defs fetch in
22
+ * total. The defs cache key includes the version, so a (highly
23
+ * unlikely) mid-request bump still produces a self-consistent view —
24
+ * the second call sees a different key and refetches.
25
+ *
26
+ * **Invalidation.** `options.byline_fields_version` is bumped by every
27
+ * `BylineSchemaRegistry` mutation (Phase 2). Each isolate independently
28
+ * reads the persisted version on the next request and compares against
29
+ * its cached version; mismatch triggers a refetch and overwrite. Other
30
+ * isolates see the change within one request after the bump propagates.
31
+ *
32
+ * **Isolated databases bypass the global cache.** Playground and DO
33
+ * preview sessions set `requestContext.dbIsIsolated = true`, signalling
34
+ * the per-request `db` points at an isolated schema that may diverge
35
+ * from the singleton. Schema-derived caches keyed by the singleton's
36
+ * version would silently leak the singleton's defs into the isolated
37
+ * request. We follow the `loader.ts:74` `getTaxonomyNames` precedent:
38
+ * skip both reading from and writing to the global holder when the
39
+ * request is isolated. The per-request cache (`requestCached`) is keyed
40
+ * by the WeakMap'd `EmDashRequestContext`, so it can't cross-pollinate
41
+ * between requests — it stays in play even for isolated DBs.
42
+ *
43
+ * **Why a versioned cache and not a TTL?** The version counter gives
44
+ * deterministic invalidation without the staleness window a TTL would
45
+ * impose. Field-definition changes need to be visible to the next
46
+ * request, not eventually. The cost is one cheap `options` read per
47
+ * request — cheaper than the field-defs fetch it replaces, and cheaper
48
+ * than maintaining a TTL state machine.
49
+ */
50
+
51
+ import type { Kysely } from "kysely";
52
+
53
+ import type { Database } from "../database/types.js";
54
+ import { requestCached } from "../request-cache.js";
55
+ import { getRequestContext } from "../request-context.js";
56
+ import { BylineSchemaRegistry } from "../schema/byline-registry.js";
57
+ import type { BylineFieldDefinition } from "../schema/types.js";
58
+
59
+ interface FieldDefsHolder {
60
+ /** In-flight or resolved defs promise for the cached version. Null until first read. */
61
+ cached: Promise<BylineFieldDefinition[]> | null;
62
+ /** Persisted-version value that `cached` was fetched against. */
63
+ cachedVersion: number;
64
+ }
65
+
66
+ const HOLDER_KEY = Symbol.for("emdash:byline-field-defs");
67
+ const g = globalThis as Record<symbol, unknown>;
68
+ const holder: FieldDefsHolder =
69
+ // eslint-disable-next-line typescript/no-unsafe-type-assertion -- globalThis singleton pattern (see request-cache.ts)
70
+ (g[HOLDER_KEY] as FieldDefsHolder | undefined) ??
71
+ (() => {
72
+ const h: FieldDefsHolder = { cached: null, cachedVersion: -1 };
73
+ g[HOLDER_KEY] = h;
74
+ return h;
75
+ })();
76
+
77
+ const REQUEST_CACHE_KEY_VERSION = "byline-fields-version";
78
+ const REQUEST_CACHE_KEY_DEFS_PREFIX = "byline-field-defs:";
79
+
80
+ /**
81
+ * Read the persisted `options.byline_fields_version` counter. Cached for
82
+ * the duration of the current request via `requestCached`. Returns `0`
83
+ * when the row is missing (matches `BylineSchemaRegistry.getVersion`).
84
+ */
85
+ async function getBylineFieldsVersion(db: Kysely<Database>): Promise<number> {
86
+ return requestCached(REQUEST_CACHE_KEY_VERSION, () => new BylineSchemaRegistry(db).getVersion());
87
+ }
88
+
89
+ /**
90
+ * Resolve registered byline custom-field definitions. Two-tier cache:
91
+ * per-request via `requestCached`, then per-isolate via the global
92
+ * holder.
93
+ *
94
+ * The global holder is bypassed for isolated requests (playground / DO
95
+ * preview, which point at a divergent schema) and for dirty versions
96
+ * (odd counter — see `BylineSchemaRegistry`'s class JSDoc — indicates
97
+ * an in-flight or crashed mutation). Both bypass paths still hit the
98
+ * per-request cache, so a single render dedupes within itself.
99
+ *
100
+ * Always returns an array. Empty = no custom fields registered.
101
+ */
102
+ export async function getBylineFieldDefs(db: Kysely<Database>): Promise<BylineFieldDefinition[]> {
103
+ const isolated = getRequestContext()?.dbIsIsolated === true;
104
+ const version = await getBylineFieldsVersion(db);
105
+ const dirty = version % 2 !== 0;
106
+ return requestCached(`${REQUEST_CACHE_KEY_DEFS_PREFIX}${version}`, async () => {
107
+ if (isolated || dirty) {
108
+ return new BylineSchemaRegistry(db).listFields();
109
+ }
110
+ if (holder.cached !== null && holder.cachedVersion === version) {
111
+ return holder.cached;
112
+ }
113
+ const defs = new BylineSchemaRegistry(db).listFields().catch((error) => {
114
+ if (holder.cached === defs) {
115
+ holder.cached = null;
116
+ holder.cachedVersion = -1;
117
+ }
118
+ throw error;
119
+ });
120
+ holder.cached = defs;
121
+ holder.cachedVersion = version;
122
+ return defs;
123
+ });
124
+ }
125
+
126
+ /**
127
+ * Test/internal helper: clear the per-isolate cache. Useful for unit
128
+ * tests that mutate the registry directly and need to force a refetch
129
+ * without going through the full version-bump path.
130
+ *
131
+ * Production code paths should rely on the version counter for
132
+ * invalidation — calling this from a write path would bypass the
133
+ * coordination that lets other isolates see the change.
134
+ */
135
+ export function resetBylineFieldDefsCacheForTests(): void {
136
+ holder.cached = null;
137
+ holder.cachedVersion = -1;
138
+ }
@@ -220,10 +220,20 @@ export async function getBylinesForEntries(
220
220
  // previous "has any bylines" probe, without the extra round-trip.
221
221
  // Pre-migration databases (bylines table missing) fall through to the
222
222
  // `isMissingTableError` catch below and return empty.
223
+ //
224
+ // Each bucket's `getContentBylinesMany` call uses `skipHydration: true`
225
+ // so the per-bucket fetches return bylines with `customFields = {}`.
226
+ // We then hydrate the union of returned bylines in a SINGLE batched
227
+ // pass via `hydrateBylineCustomFields`. This keeps mixed-locale list
228
+ // hydration at one batched group-shared query (and one batched
229
+ // translatable query) per request, even when locale buckets reference
230
+ // disjoint translation_groups — the strict reading of the Phase 3
231
+ // query-count envelope.
223
232
  const explicitByEntry = new Map<string, ContentBylineCredit[]>();
224
233
  const entriesNeedingAuthorCheck: BylineEntry[] = [];
234
+ const hydrationTargets: BylineSummary[] = [];
225
235
  for (const [locale, bucket] of buckets) {
226
- const localeOpt = locale ? { locale } : undefined;
236
+ const localeOpt = locale ? { locale, skipHydration: true } : { skipHydration: true };
227
237
  const bucketIds = bucket.map((e) => e.id);
228
238
  let bylinesMap;
229
239
  try {
@@ -232,7 +242,10 @@ export async function getBylinesForEntries(
232
242
  if (isMissingTableError(error)) return result;
233
243
  throw error;
234
244
  }
235
- for (const [id, list] of bylinesMap) explicitByEntry.set(id, list);
245
+ for (const [id, list] of bylinesMap) {
246
+ explicitByEntry.set(id, list);
247
+ for (const credit of list) hydrationTargets.push(credit.byline);
248
+ }
236
249
 
237
250
  for (const entry of bucket) {
238
251
  const hasResolved = bylinesMap.has(entry.id) && bylinesMap.get(entry.id)!.length > 0;
@@ -255,19 +268,39 @@ export async function getBylinesForEntries(
255
268
  }
256
269
 
257
270
  for (const [locale, bucket] of authorBuckets) {
258
- const localeOpt = locale ? { locale } : undefined;
271
+ const localeOpt: { locale?: string; skipHydration: true } = locale
272
+ ? { locale, skipHydration: true }
273
+ : { skipHydration: true };
259
274
  const authorIds = bucket.map((e) => e.authorId).filter((id): id is string => id !== null);
260
275
  const uniqueAuthorIds = [...new Set(authorIds)];
261
276
  if (uniqueAuthorIds.length === 0) continue;
277
+ // `skipHydration: true` returns bylines with `customFields = {}`
278
+ // so the fallback path participates in the single batched
279
+ // `hydrateBylineCustomFields` call below — keeping the query
280
+ // envelope at "+1 group-shared query per hydration pass" even
281
+ // when author bylines across locale buckets reference disjoint
282
+ // translation_groups.
262
283
  const authorBylineMap = await repo.findByUserIds(uniqueAuthorIds, localeOpt);
263
284
  for (const entry of bucket) {
264
285
  if (!entry.authorId) continue;
265
286
  const f = authorBylineMap.get(entry.authorId);
266
- if (f) fallbackByEntry.set(entry.id, f);
287
+ if (f) {
288
+ fallbackByEntry.set(entry.id, f);
289
+ hydrationTargets.push(f);
290
+ }
267
291
  }
268
292
  }
269
293
  }
270
294
 
295
+ // Single batched hydration over every byline returned from both the
296
+ // per-bucket explicit-credit fetches AND the per-bucket author-
297
+ // fallback fetches. One translatable query + one group-shared query
298
+ // for the whole pass, regardless of bucket count or whether
299
+ // translation_groups overlap across locales.
300
+ if (hydrationTargets.length > 0) {
301
+ await repo.hydrateBylineCustomFields(hydrationTargets);
302
+ }
303
+
271
304
  for (const { id } of entries) {
272
305
  const explicit = explicitByEntry.get(id);
273
306
  if (explicit && explicit.length > 0) {
@@ -233,6 +233,7 @@ const updateCommand = defineCommand({
233
233
  description: "Revision token from get (prevents overwriting unseen changes)",
234
234
  required: true,
235
235
  },
236
+ locale: { type: "string", description: "Locale for slug resolution" },
236
237
  draft: {
237
238
  type: "boolean",
238
239
  description: "Keep as draft instead of auto-publishing",
@@ -247,17 +248,18 @@ const updateCommand = defineCommand({
247
248
  const updated = await client.update(args.collection, args.id, {
248
249
  data,
249
250
  _rev: args.rev,
251
+ locale: args.locale,
250
252
  });
251
253
 
252
254
  // Auto-publish unless --draft is set.
253
255
  // Only publish if the update created a draft revision (i.e. the
254
256
  // collection supports revisions and data went to a draft).
255
257
  if (!args.draft && updated.draftRevisionId) {
256
- await client.publish(args.collection, args.id);
258
+ await client.publish(args.collection, updated.id);
257
259
  }
258
260
 
259
261
  // Re-fetch to return the current state
260
- const item = await client.get(args.collection, args.id);
262
+ const item = await client.get(args.collection, updated.id);
261
263
  output(item, args);
262
264
  } catch (error) {
263
265
  consola.error(error instanceof Error ? error.message : "Unknown error");