emdash 0.35.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (971) hide show
  1. package/dist/activation-K0kGBWuJ.mjs +382 -0
  2. package/dist/activation-K0kGBWuJ.mjs.map +1 -0
  3. package/dist/{adapters-Bb1vG41D.d.mts → adapters-BDQwg236.d.mts} +6 -4
  4. package/dist/{adapters-Bb1vG41D.d.mts.map → adapters-BDQwg236.d.mts.map} +1 -1
  5. package/dist/{allowed-origins-DW7sYyqg.mjs → allowed-origins-BGglilcs.mjs} +2 -2
  6. package/dist/{allowed-origins-DW7sYyqg.mjs.map → allowed-origins-BGglilcs.mjs.map} +1 -1
  7. package/dist/api/route-utils.d.mts +3 -3
  8. package/dist/api/route-utils.mjs +23 -21
  9. package/dist/api/route-utils.mjs.map +1 -1
  10. package/dist/api/schemas/index.d.mts +2 -2
  11. package/dist/api/schemas/index.mjs +5 -5
  12. package/dist/api-Dz6rgoa3.mjs +10040 -0
  13. package/dist/api-Dz6rgoa3.mjs.map +1 -0
  14. package/dist/{api-tokens-KO24G1Fd.mjs → api-tokens-BSsQACNe.mjs} +2 -2
  15. package/dist/{api-tokens-KO24G1Fd.mjs.map → api-tokens-BSsQACNe.mjs.map} +1 -1
  16. package/dist/{apply-kUDt80-G.mjs → apply-B4SIOxR1.mjs} +34 -35
  17. package/dist/{apply-kUDt80-G.mjs.map → apply-B4SIOxR1.mjs.map} +1 -1
  18. package/dist/astro/image-endpoint.mjs +2 -2
  19. package/dist/astro/image-endpoint.mjs.map +1 -1
  20. package/dist/astro/index.d.mts +12 -12
  21. package/dist/astro/index.d.mts.map +1 -1
  22. package/dist/astro/index.mjs +128 -44
  23. package/dist/astro/index.mjs.map +1 -1
  24. package/dist/astro/middleware/auth.d.mts +10 -10
  25. package/dist/astro/middleware/auth.d.mts.map +1 -1
  26. package/dist/astro/middleware/auth.mjs +10 -7
  27. package/dist/astro/middleware/auth.mjs.map +1 -1
  28. package/dist/astro/middleware/media-usage-write-fence.mjs +2 -2
  29. package/dist/astro/middleware/redirect.mjs +4 -4
  30. package/dist/astro/middleware/request-context.mjs +3 -3
  31. package/dist/astro/middleware/setup.mjs +1 -1
  32. package/dist/astro/middleware.d.mts +11 -12
  33. package/dist/astro/middleware.d.mts.map +1 -1
  34. package/dist/astro/middleware.mjs +237 -1412
  35. package/dist/astro/middleware.mjs.map +1 -1
  36. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +6 -6
  37. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +6 -6
  38. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +3 -3
  39. package/dist/astro/routes/api/admin/api-tokens/index.d.mts.map +1 -1
  40. package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
  41. package/dist/astro/routes/api/admin/api-tokens/index.mjs.map +1 -1
  42. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
  43. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +9 -9
  44. package/dist/astro/routes/api/admin/byline-fields/index.mjs +9 -9
  45. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +9 -9
  46. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +16 -16
  47. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +16 -16
  48. package/dist/astro/routes/api/admin/bylines/index.mjs +16 -16
  49. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +13 -13
  50. package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
  51. package/dist/astro/routes/api/admin/comments/bulk.mjs +10 -10
  52. package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
  53. package/dist/astro/routes/api/admin/comments/index.mjs +10 -10
  54. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
  55. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  56. package/dist/astro/routes/api/admin/media-usage/activation.d.mts +9 -0
  57. package/dist/astro/routes/api/admin/media-usage/activation.d.mts.map +1 -0
  58. package/dist/astro/routes/api/admin/media-usage/activation.mjs +46 -0
  59. package/dist/astro/routes/api/admin/media-usage/activation.mjs.map +1 -0
  60. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs +14 -13
  61. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs.map +1 -1
  62. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs +14 -13
  63. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs.map +1 -1
  64. package/dist/astro/routes/api/admin/media-usage/progress.d.mts +9 -0
  65. package/dist/astro/routes/api/admin/media-usage/progress.d.mts.map +1 -0
  66. package/dist/astro/routes/api/admin/media-usage/progress.mjs +44 -0
  67. package/dist/astro/routes/api/admin/media-usage/progress.mjs.map +1 -0
  68. package/dist/astro/routes/api/admin/media-usage/repair.mjs +16 -11
  69. package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -1
  70. package/dist/astro/routes/api/admin/media-usage/work/index.mjs +14 -13
  71. package/dist/astro/routes/api/admin/media-usage/work/index.mjs.map +1 -1
  72. package/dist/astro/routes/api/admin/media-usage/work/retry.mjs +14 -13
  73. package/dist/astro/routes/api/admin/media-usage/work/retry.mjs.map +1 -1
  74. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +4 -4
  75. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs.map +1 -1
  76. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
  77. package/dist/astro/routes/api/admin/oauth-clients/index.mjs.map +1 -1
  78. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +55 -49
  79. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  80. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +55 -49
  81. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  82. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +53 -47
  83. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  84. package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +4 -4
  85. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +53 -47
  86. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
  87. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +54 -48
  88. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  89. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +54 -48
  90. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  91. package/dist/astro/routes/api/admin/plugins/index.mjs +53 -47
  92. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  93. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
  94. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +53 -47
  95. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  96. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +54 -48
  97. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  98. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +53 -47
  99. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  100. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +54 -48
  101. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  102. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.d.mts.map +1 -1
  103. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +60 -50
  104. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  105. package/dist/astro/routes/api/admin/plugins/registry/artifact.d.mts.map +1 -1
  106. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +179 -139
  107. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  108. package/dist/astro/routes/api/admin/plugins/registry/install.d.mts.map +1 -1
  109. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +61 -51
  110. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  111. package/dist/astro/routes/api/admin/plugins/registry/verify.d.mts +8 -0
  112. package/dist/astro/routes/api/admin/plugins/registry/verify.d.mts.map +1 -0
  113. package/dist/astro/routes/api/admin/plugins/registry/verify.mjs +93 -0
  114. package/dist/astro/routes/api/admin/plugins/registry/verify.mjs.map +1 -0
  115. package/dist/astro/routes/api/admin/plugins/updates.mjs +53 -47
  116. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  117. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +53 -47
  118. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  119. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +2 -2
  120. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +53 -47
  121. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  122. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
  123. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +1 -1
  124. package/dist/astro/routes/api/admin/users/_id_/index.mjs +7 -7
  125. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +3 -3
  126. package/dist/astro/routes/api/admin/users/index.mjs +6 -6
  127. package/dist/astro/routes/api/auth/dev-bypass.mjs +3 -3
  128. package/dist/astro/routes/api/auth/invite/accept.mjs +1 -1
  129. package/dist/astro/routes/api/auth/invite/complete.mjs +11 -11
  130. package/dist/astro/routes/api/auth/invite/index.mjs +8 -8
  131. package/dist/astro/routes/api/auth/invite/register-options.mjs +10 -10
  132. package/dist/astro/routes/api/auth/logout.mjs +2 -2
  133. package/dist/astro/routes/api/auth/magic-link/send.mjs +10 -10
  134. package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
  135. package/dist/astro/routes/api/auth/me.d.mts.map +1 -1
  136. package/dist/astro/routes/api/auth/me.mjs +19 -9
  137. package/dist/astro/routes/api/auth/me.mjs.map +1 -1
  138. package/dist/astro/routes/api/auth/mode.mjs +2 -2
  139. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
  140. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  141. package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -6
  142. package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
  143. package/dist/astro/routes/api/auth/passkey/options.mjs +12 -12
  144. package/dist/astro/routes/api/auth/passkey/register/options.mjs +10 -10
  145. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +11 -11
  146. package/dist/astro/routes/api/auth/passkey/verify.mjs +11 -11
  147. package/dist/astro/routes/api/auth/signup/complete.mjs +11 -11
  148. package/dist/astro/routes/api/auth/signup/request.mjs +10 -10
  149. package/dist/astro/routes/api/auth/signup/verify.mjs +1 -1
  150. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +14 -14
  151. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +13 -13
  152. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
  153. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.d.mts.map +1 -1
  154. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +14 -5
  155. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs.map +1 -1
  156. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
  157. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +2 -2
  158. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +11 -11
  159. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +13 -10
  160. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs.map +1 -1
  161. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +17 -15
  162. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -1
  163. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +17 -15
  164. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -1
  165. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
  166. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
  167. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +8 -8
  168. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +14 -14
  169. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
  170. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.d.mts.map +1 -1
  171. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +14 -5
  172. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs.map +1 -1
  173. package/dist/astro/routes/api/content/_collection_/_id_.mjs +8 -8
  174. package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
  175. package/dist/astro/routes/api/content/_collection_/index.mjs +8 -8
  176. package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -7
  177. package/dist/astro/routes/api/dashboard.mjs +8 -8
  178. package/dist/astro/routes/api/dev/emails.mjs +2 -2
  179. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  180. package/dist/astro/routes/api/import/probe.mjs +17 -17
  181. package/dist/astro/routes/api/import/wordpress/analyze.mjs +2 -2
  182. package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
  183. package/dist/astro/routes/api/import/wordpress/execute.mjs +13 -13
  184. package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -10
  185. package/dist/astro/routes/api/import/wordpress/media.mjs +11 -11
  186. package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
  187. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -11
  188. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +12 -10
  189. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -1
  190. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  191. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +17 -17
  192. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
  193. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +27 -27
  194. package/dist/astro/routes/api/manifest.mjs +4 -4
  195. package/dist/astro/routes/api/manifest.mjs.map +1 -1
  196. package/dist/astro/routes/api/mcp.mjs +188 -65
  197. package/dist/astro/routes/api/mcp.mjs.map +1 -1
  198. package/dist/astro/routes/api/media/_id_/confirm.mjs +11 -11
  199. package/dist/astro/routes/api/media/_id_/replace.d.mts +8 -0
  200. package/dist/astro/routes/api/media/_id_/replace.d.mts.map +1 -0
  201. package/dist/astro/routes/api/media/_id_/replace.mjs +91 -0
  202. package/dist/astro/routes/api/media/_id_/replace.mjs.map +1 -0
  203. package/dist/astro/routes/api/media/_id_/upload.mjs +6 -6
  204. package/dist/astro/routes/api/media/_id_/usage.mjs +18 -13
  205. package/dist/astro/routes/api/media/_id_/usage.mjs.map +1 -1
  206. package/dist/astro/routes/api/media/_id_.d.mts.map +1 -1
  207. package/dist/astro/routes/api/media/_id_.mjs +21 -13
  208. package/dist/astro/routes/api/media/_id_.mjs.map +1 -1
  209. package/dist/astro/routes/api/media/file/_...key_.d.mts.map +1 -1
  210. package/dist/astro/routes/api/media/file/_...key_.mjs +3 -2
  211. package/dist/astro/routes/api/media/file/_...key_.mjs.map +1 -1
  212. package/dist/astro/routes/api/media/folders/_id_.d.mts +10 -0
  213. package/dist/astro/routes/api/media/folders/_id_.d.mts.map +1 -0
  214. package/dist/astro/routes/api/media/folders/_id_.mjs +52 -0
  215. package/dist/astro/routes/api/media/folders/_id_.mjs.map +1 -0
  216. package/dist/astro/routes/api/media/folders/index.d.mts +9 -0
  217. package/dist/astro/routes/api/media/folders/index.d.mts.map +1 -0
  218. package/dist/astro/routes/api/media/folders/index.mjs +41 -0
  219. package/dist/astro/routes/api/media/folders/index.mjs.map +1 -0
  220. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
  221. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
  222. package/dist/astro/routes/api/media/providers/index.mjs +2 -2
  223. package/dist/astro/routes/api/media/upload-url.mjs +16 -14
  224. package/dist/astro/routes/api/media/upload-url.mjs.map +1 -1
  225. package/dist/astro/routes/api/media.d.mts.map +1 -1
  226. package/dist/astro/routes/api/media.mjs +59 -27
  227. package/dist/astro/routes/api/media.mjs.map +1 -1
  228. package/dist/astro/routes/api/menus/_name_/items/_id_.d.mts.map +1 -1
  229. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +20 -13
  230. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs.map +1 -1
  231. package/dist/astro/routes/api/menus/_name_/items.d.mts.map +1 -1
  232. package/dist/astro/routes/api/menus/_name_/items.mjs +15 -11
  233. package/dist/astro/routes/api/menus/_name_/items.mjs.map +1 -1
  234. package/dist/astro/routes/api/menus/_name_/reorder.d.mts.map +1 -1
  235. package/dist/astro/routes/api/menus/_name_/reorder.mjs +15 -11
  236. package/dist/astro/routes/api/menus/_name_/reorder.mjs.map +1 -1
  237. package/dist/astro/routes/api/menus/_name_/translations.d.mts.map +1 -1
  238. package/dist/astro/routes/api/menus/_name_/translations.mjs +16 -12
  239. package/dist/astro/routes/api/menus/_name_/translations.mjs.map +1 -1
  240. package/dist/astro/routes/api/menus/_name_.d.mts.map +1 -1
  241. package/dist/astro/routes/api/menus/_name_.mjs +21 -14
  242. package/dist/astro/routes/api/menus/_name_.mjs.map +1 -1
  243. package/dist/astro/routes/api/menus/index.d.mts.map +1 -1
  244. package/dist/astro/routes/api/menus/index.mjs +15 -11
  245. package/dist/astro/routes/api/menus/index.mjs.map +1 -1
  246. package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
  247. package/dist/astro/routes/api/oauth/device/authorize.mjs +5 -5
  248. package/dist/astro/routes/api/oauth/device/code.mjs +8 -8
  249. package/dist/astro/routes/api/oauth/device/token.mjs +7 -7
  250. package/dist/astro/routes/api/oauth/register.mjs +2 -2
  251. package/dist/astro/routes/api/oauth/token/refresh.mjs +5 -5
  252. package/dist/astro/routes/api/oauth/token/revoke.mjs +5 -5
  253. package/dist/astro/routes/api/oauth/token.mjs +5 -5
  254. package/dist/astro/routes/api/openapi.json.mjs +253 -40
  255. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  256. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +4 -4
  257. package/dist/astro/routes/api/redirects/404s/index.mjs +10 -10
  258. package/dist/astro/routes/api/redirects/404s/summary.mjs +10 -10
  259. package/dist/astro/routes/api/redirects/_id_.mjs +11 -11
  260. package/dist/astro/routes/api/redirects/index.mjs +11 -11
  261. package/dist/astro/routes/api/relations/_id_/index.mjs +17 -15
  262. package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
  263. package/dist/astro/routes/api/relations/_id_/translations.mjs +12 -10
  264. package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -1
  265. package/dist/astro/routes/api/relations/index.mjs +17 -15
  266. package/dist/astro/routes/api/relations/index.mjs.map +1 -1
  267. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +2 -2
  268. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
  269. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +53 -47
  270. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  271. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +53 -47
  272. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  273. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +53 -47
  274. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  275. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +53 -47
  276. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  277. package/dist/astro/routes/api/schema/collections/index.mjs +53 -47
  278. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  279. package/dist/astro/routes/api/schema/collections/reorder.mjs +53 -47
  280. package/dist/astro/routes/api/schema/collections/reorder.mjs.map +1 -1
  281. package/dist/astro/routes/api/schema/index.mjs +10 -8
  282. package/dist/astro/routes/api/schema/index.mjs.map +1 -1
  283. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +53 -47
  284. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  285. package/dist/astro/routes/api/schema/orphans/index.mjs +53 -47
  286. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  287. package/dist/astro/routes/api/search/enable.mjs +10 -10
  288. package/dist/astro/routes/api/search/index.mjs +9 -9
  289. package/dist/astro/routes/api/search/rebuild.mjs +10 -10
  290. package/dist/astro/routes/api/search/stats.mjs +5 -5
  291. package/dist/astro/routes/api/search/suggest.mjs +9 -9
  292. package/dist/astro/routes/api/sections/_slug_.mjs +9 -9
  293. package/dist/astro/routes/api/sections/index.mjs +9 -9
  294. package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +6 -6
  295. package/dist/astro/routes/api/settings/backups/archives/index.mjs +5 -5
  296. package/dist/astro/routes/api/settings/backups/export.mjs +6 -6
  297. package/dist/astro/routes/api/settings/backups/index.mjs +7 -7
  298. package/dist/astro/routes/api/settings/email.mjs +5 -5
  299. package/dist/astro/routes/api/settings/email.mjs.map +1 -1
  300. package/dist/astro/routes/api/settings.d.mts.map +1 -1
  301. package/dist/astro/routes/api/settings.mjs +20 -16
  302. package/dist/astro/routes/api/settings.mjs.map +1 -1
  303. package/dist/astro/routes/api/setup/admin-verify.mjs +12 -12
  304. package/dist/astro/routes/api/setup/admin.mjs +11 -11
  305. package/dist/astro/routes/api/setup/dev-bypass.d.mts.map +1 -1
  306. package/dist/astro/routes/api/setup/dev-bypass.mjs +35 -28
  307. package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
  308. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  309. package/dist/astro/routes/api/setup/index.mjs +32 -30
  310. package/dist/astro/routes/api/setup/index.mjs.map +1 -1
  311. package/dist/astro/routes/api/setup/status.mjs +3 -3
  312. package/dist/astro/routes/api/snapshot.mjs +7 -7
  313. package/dist/astro/routes/api/taxonomies/_name_/reorder.d.mts.map +1 -1
  314. package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +20 -16
  315. package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs.map +1 -1
  316. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.d.mts.map +1 -1
  317. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +21 -17
  318. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
  319. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.d.mts.map +1 -1
  320. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +25 -18
  321. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
  322. package/dist/astro/routes/api/taxonomies/_name_/terms/index.d.mts.map +1 -1
  323. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +20 -16
  324. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
  325. package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +14 -14
  326. package/dist/astro/routes/api/taxonomies/_name_.d.mts.map +1 -1
  327. package/dist/astro/routes/api/taxonomies/_name_.mjs +26 -19
  328. package/dist/astro/routes/api/taxonomies/_name_.mjs.map +1 -1
  329. package/dist/astro/routes/api/taxonomies/index.d.mts.map +1 -1
  330. package/dist/astro/routes/api/taxonomies/index.mjs +20 -16
  331. package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -1
  332. package/dist/astro/routes/api/themes/preview.mjs +5 -5
  333. package/dist/astro/routes/api/typegen.mjs +4 -4
  334. package/dist/astro/routes/api/well-known/auth.mjs +2 -2
  335. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  336. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  337. package/dist/astro/routes/api/widget-areas/_name_/reorder.d.mts.map +1 -1
  338. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +10 -8
  339. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs.map +1 -1
  340. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.d.mts.map +1 -1
  341. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +17 -13
  342. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -1
  343. package/dist/astro/routes/api/widget-areas/_name_/widgets.d.mts.map +1 -1
  344. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +15 -12
  345. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -1
  346. package/dist/astro/routes/api/widget-areas/_name_.d.mts.map +1 -1
  347. package/dist/astro/routes/api/widget-areas/_name_.mjs +8 -6
  348. package/dist/astro/routes/api/widget-areas/_name_.mjs.map +1 -1
  349. package/dist/astro/routes/api/widget-areas/index.d.mts.map +1 -1
  350. package/dist/astro/routes/api/widget-areas/index.mjs +15 -12
  351. package/dist/astro/routes/api/widget-areas/index.mjs.map +1 -1
  352. package/dist/astro/routes/api/widget-components.mjs +2 -2
  353. package/dist/astro/routes/robots.txt.mjs +7 -7
  354. package/dist/astro/routes/sitemap-_collection_.xml.mjs +10 -10
  355. package/dist/astro/routes/sitemap.xml.mjs +8 -8
  356. package/dist/astro/types.d.mts +37 -17
  357. package/dist/astro/types.d.mts.map +1 -1
  358. package/dist/auth/providers/github.d.mts +1 -1
  359. package/dist/auth/providers/google.d.mts +1 -1
  360. package/dist/{authorize-DUElxgOQ.mjs → authorize-Bkf_UHhD.mjs} +2 -2
  361. package/dist/{authorize-DUElxgOQ.mjs.map → authorize-Bkf_UHhD.mjs.map} +1 -1
  362. package/dist/{backup-DWYpJlLj.mjs → backup-rwVAl9NI.mjs} +5 -5
  363. package/dist/{backup-DWYpJlLj.mjs.map → backup-rwVAl9NI.mjs.map} +1 -1
  364. package/dist/{bundle-vn56sLos.mjs → bundle-C8S2Zl6U.mjs} +4 -4
  365. package/dist/{bundle-vn56sLos.mjs.map → bundle-C8S2Zl6U.mjs.map} +1 -1
  366. package/dist/{byline-yjvWq4ip.mjs → byline-DuhF7NWl.mjs} +27 -14
  367. package/dist/byline-DuhF7NWl.mjs.map +1 -0
  368. package/dist/{byline-fields-txaLGoME.mjs → byline-fields-BovY4KYM.mjs} +2 -2
  369. package/dist/{byline-fields-txaLGoME.mjs.map → byline-fields-BovY4KYM.mjs.map} +1 -1
  370. package/dist/{byline-registry-DDun8sxh.mjs → byline-registry-Bpu9-MVs.mjs} +2 -2
  371. package/dist/{byline-registry-DDun8sxh.mjs.map → byline-registry-Bpu9-MVs.mjs.map} +1 -1
  372. package/dist/{bylines-DjXS5p0P.mjs → bylines-CJ5nd6Lw.mjs} +7 -7
  373. package/dist/{bylines-DjXS5p0P.mjs.map → bylines-CJ5nd6Lw.mjs.map} +1 -1
  374. package/dist/{bylines-Bu7pchQ4.mjs → bylines-RwBAeOF1.mjs} +3 -3
  375. package/dist/{bylines-Bu7pchQ4.mjs.map → bylines-RwBAeOF1.mjs.map} +1 -1
  376. package/dist/{cache-C46-3Irl.mjs → cache-BF5Kvn59.mjs} +4 -4
  377. package/dist/{cache-C46-3Irl.mjs.map → cache-BF5Kvn59.mjs.map} +1 -1
  378. package/dist/capture-triggers-CNW1q3HK.mjs +362 -0
  379. package/dist/capture-triggers-CNW1q3HK.mjs.map +1 -0
  380. package/dist/{challenge-store-SIL5DsF2.mjs → challenge-store-DQN_Yrck.mjs} +5 -3
  381. package/dist/challenge-store-DQN_Yrck.mjs.map +1 -0
  382. package/dist/chrome-tags-D8fiZG94.mjs +24 -0
  383. package/dist/chrome-tags-D8fiZG94.mjs.map +1 -0
  384. package/dist/{chunks-fiZ8G9RJ.mjs → chunks-C4szrD8o.mjs} +2 -2
  385. package/dist/{chunks-fiZ8G9RJ.mjs.map → chunks-C4szrD8o.mjs.map} +1 -1
  386. package/dist/cli/index.mjs +37 -53
  387. package/dist/cli/index.mjs.map +1 -1
  388. package/dist/client/cf-access.d.mts +1 -1
  389. package/dist/client/index.d.mts +74 -4
  390. package/dist/client/index.d.mts.map +1 -1
  391. package/dist/client/index.mjs +54 -2
  392. package/dist/client/index.mjs.map +1 -1
  393. package/dist/{collection-deletion-DQPvMD8R.mjs → collection-deletion-Bj07NHbj.mjs} +13 -356
  394. package/dist/collection-deletion-Bj07NHbj.mjs.map +1 -0
  395. package/dist/{comment-reaction-D-2J84UE.mjs → comment-reaction-BhrvSHml.mjs} +2 -2
  396. package/dist/{comment-reaction-D-2J84UE.mjs.map → comment-reaction-BhrvSHml.mjs.map} +1 -1
  397. package/dist/{comment-Dxlep6cE.mjs → comment-vxOzWbZN.mjs} +2 -2
  398. package/dist/{comment-Dxlep6cE.mjs.map → comment-vxOzWbZN.mjs.map} +1 -1
  399. package/dist/{comments-CTfZtLmM.mjs → comments-Ct4ensnf.mjs} +2 -2
  400. package/dist/{comments-CTfZtLmM.mjs.map → comments-Ct4ensnf.mjs.map} +1 -1
  401. package/dist/{components-CQy3lh9x.mjs → components-DJbo1z0S.mjs} +1 -1
  402. package/dist/{components-CQy3lh9x.mjs.map → components-DJbo1z0S.mjs.map} +1 -1
  403. package/dist/{config-COQzTe5Q.d.mts → config-B9eZ340f.d.mts} +1 -1
  404. package/dist/{config-COQzTe5Q.d.mts.map → config-B9eZ340f.d.mts.map} +1 -1
  405. package/dist/config-it29yDLA.mjs +1163 -0
  406. package/dist/config-it29yDLA.mjs.map +1 -0
  407. package/dist/{content-ZHCc1ngN.mjs → content-DjvM0T92.mjs} +86 -45
  408. package/dist/content-DjvM0T92.mjs.map +1 -0
  409. package/dist/{content-list-query-wJ2WN8Vp.d.mts → content-list-query-Btqd4uV2.d.mts} +1 -1
  410. package/dist/{content-list-query-wJ2WN8Vp.d.mts.map → content-list-query-Btqd4uV2.d.mts.map} +1 -1
  411. package/dist/{content-refresh-B_8SJzuU.mjs → content-refresh-Bwp-8yGy.mjs} +1139 -111
  412. package/dist/content-refresh-Bwp-8yGy.mjs.map +1 -0
  413. package/dist/{media-usage-BZCydfvu.mjs → content-repair-Czdp7jz5.mjs} +5 -196
  414. package/dist/content-repair-Czdp7jz5.mjs.map +1 -0
  415. package/dist/{context-ZldbZcle.mjs → context-CX6gxOLW.mjs} +55 -20
  416. package/dist/context-CX6gxOLW.mjs.map +1 -0
  417. package/dist/{cron-Y9aLtgrA.mjs → cron-C6o7VppJ.mjs} +1 -1
  418. package/dist/{cron-Y9aLtgrA.mjs.map → cron-C6o7VppJ.mjs.map} +1 -1
  419. package/dist/{dashboard-DvHbk_cv.mjs → dashboard-C2kTeeRI.mjs} +5 -5
  420. package/dist/{dashboard-DvHbk_cv.mjs.map → dashboard-C2kTeeRI.mjs.map} +1 -1
  421. package/dist/db/index.d.mts +3 -3
  422. package/dist/db/index.mjs +3 -3
  423. package/dist/db/index.mjs.map +1 -1
  424. package/dist/db/libsql-migrations.d.mts +1 -1
  425. package/dist/db/libsql-migrations.mjs +4 -4
  426. package/dist/db/libsql.d.mts +1 -1
  427. package/dist/db/postgres-migrations.d.mts +1 -1
  428. package/dist/db/postgres-migrations.mjs +4 -4
  429. package/dist/db/postgres.d.mts +1 -1
  430. package/dist/db/postgres.d.mts.map +1 -1
  431. package/dist/db/postgres.mjs +19 -3
  432. package/dist/db/postgres.mjs.map +1 -1
  433. package/dist/db/sqlite-migrations.d.mts +1 -1
  434. package/dist/db/sqlite-migrations.mjs +4 -4
  435. package/dist/db/sqlite.d.mts +1 -1
  436. package/dist/db/sqlite.mjs +3 -3
  437. package/dist/db/sqlite.mjs.map +1 -1
  438. package/dist/{default-DdDl3fxv.mjs → default-Clra32mw.mjs} +1 -1
  439. package/dist/{default-DdDl3fxv.mjs.map → default-Clra32mw.mjs.map} +1 -1
  440. package/dist/{deferred-tasks-SDWxrgzJ.d.mts → deferred-tasks-CvDETTJO.d.mts} +1 -1
  441. package/dist/{deferred-tasks-SDWxrgzJ.d.mts.map → deferred-tasks-CvDETTJO.d.mts.map} +1 -1
  442. package/dist/dev-typegen-DzOPfsNj.mjs +29 -0
  443. package/dist/dev-typegen-DzOPfsNj.mjs.map +1 -0
  444. package/dist/{device-flow-C0GdD_z8.mjs → device-flow-D2oCWvLJ.mjs} +5 -5
  445. package/dist/{device-flow-C0GdD_z8.mjs.map → device-flow-D2oCWvLJ.mjs.map} +1 -1
  446. package/dist/{direct-executor-CDXeJCSs.mjs → direct-executor-Bfa5_ToE.mjs} +3 -3
  447. package/dist/{direct-executor-CDXeJCSs.mjs.map → direct-executor-Bfa5_ToE.mjs.map} +1 -1
  448. package/dist/{dist-D_6CKR8z.mjs → dist-89xCrGqi.mjs} +1 -1
  449. package/dist/{dist-D_6CKR8z.mjs.map → dist-89xCrGqi.mjs.map} +1 -1
  450. package/dist/{email-console-BHNjIwiB.mjs → email-console-Dzb6uGYa.mjs} +1 -1
  451. package/dist/{email-console-BHNjIwiB.mjs.map → email-console-Dzb6uGYa.mjs.map} +1 -1
  452. package/dist/{enrich-_ySMEvEE.mjs → enrich-BX0IDp4z.mjs} +3 -3
  453. package/dist/{enrich-_ySMEvEE.mjs.map → enrich-BX0IDp4z.mjs.map} +1 -1
  454. package/dist/{error-DupyJZE2.mjs → error-DFrQjppn.mjs} +2 -2
  455. package/dist/{error-DupyJZE2.mjs.map → error-DFrQjppn.mjs.map} +1 -1
  456. package/dist/{errors-CLUPm7aD.mjs → errors-DsVoDUjJ.mjs} +24 -2
  457. package/dist/errors-DsVoDUjJ.mjs.map +1 -0
  458. package/dist/{escape-DUY2r2Zs.mjs → escape-HmHTC1Is.mjs} +1 -1
  459. package/dist/{escape-DUY2r2Zs.mjs.map → escape-HmHTC1Is.mjs.map} +1 -1
  460. package/dist/{field-defs-cache-BzwBlJrR.mjs → field-defs-cache-BM4SIRqH.mjs} +5 -5
  461. package/dist/{field-defs-cache-BzwBlJrR.mjs.map → field-defs-cache-BM4SIRqH.mjs.map} +1 -1
  462. package/dist/focal-point-BeWFo6Xx.mjs +23 -0
  463. package/dist/focal-point-BeWFo6Xx.mjs.map +1 -0
  464. package/dist/{fts-manager-DyZEohZC.mjs → fts-manager-Do1BlW2r.mjs} +3 -3
  465. package/dist/{fts-manager-DyZEohZC.mjs.map → fts-manager-Do1BlW2r.mjs.map} +1 -1
  466. package/dist/{hash-Cg0TXJPV.mjs → hash-3JJPlHF2.mjs} +1 -1
  467. package/dist/{hash-Cg0TXJPV.mjs.map → hash-3JJPlHF2.mjs.map} +1 -1
  468. package/dist/hook-result-DCh2WUpe.d.mts +31 -0
  469. package/dist/hook-result-DCh2WUpe.d.mts.map +1 -0
  470. package/dist/{hreflang-DwT2E0Sa.d.mts → hreflang-B6hbAuuO.d.mts} +2 -2
  471. package/dist/{hreflang-DwT2E0Sa.d.mts.map → hreflang-B6hbAuuO.d.mts.map} +1 -1
  472. package/dist/{hreflang-BBe83kKa.mjs → hreflang-DQbPZliA.mjs} +7 -7
  473. package/dist/{hreflang-BBe83kKa.mjs.map → hreflang-DQbPZliA.mjs.map} +1 -1
  474. package/dist/{identity-Ys6Dlc9C.mjs → identity-DksvEazf.mjs} +3 -3
  475. package/dist/{identity-Ys6Dlc9C.mjs.map → identity-DksvEazf.mjs.map} +1 -1
  476. package/dist/{import-DPkLmeXO.mjs → import-CJpaBBBT.mjs} +6 -6
  477. package/dist/{import-DPkLmeXO.mjs.map → import-CJpaBBBT.mjs.map} +1 -1
  478. package/dist/{index-BVBZj4-7.d.mts → index-BJNMCWmu.d.mts} +267 -82
  479. package/dist/index-BJNMCWmu.d.mts.map +1 -0
  480. package/dist/{index-C9KuQyO-.d.mts → index-Du8K7bWa.d.mts} +3 -3
  481. package/dist/{index-C9KuQyO-.d.mts.map → index-Du8K7bWa.d.mts.map} +1 -1
  482. package/dist/index.d.mts +22 -21
  483. package/dist/index.mjs +79 -73
  484. package/dist/{init-lock-6b309ZrF.mjs → init-lock-DJkX6Hto.mjs} +1 -1
  485. package/dist/{init-lock-6b309ZrF.mjs.map → init-lock-DJkX6Hto.mjs.map} +1 -1
  486. package/dist/{load-YJ2xl3ra.mjs → load-CawHjTOH.mjs} +2 -2
  487. package/dist/{load-YJ2xl3ra.mjs.map → load-CawHjTOH.mjs.map} +1 -1
  488. package/dist/{loader-CdG_Ci3H.mjs → loader-Z8vl7zTq.mjs} +48 -13
  489. package/dist/loader-Z8vl7zTq.mjs.map +1 -0
  490. package/dist/{manifest-builder-B252yQUf.mjs → manifest-builder-COtzuca2.mjs} +2 -2
  491. package/dist/{manifest-builder-B252yQUf.mjs.map → manifest-builder-COtzuca2.mjs.map} +1 -1
  492. package/dist/{manifest-schema-BXbUAHod.mjs → manifest-schema-BNWwR8Vd.mjs} +5 -5
  493. package/dist/manifest-schema-BNWwR8Vd.mjs.map +1 -0
  494. package/dist/media/image-endpoint.d.mts +4 -2
  495. package/dist/media/image-endpoint.d.mts.map +1 -1
  496. package/dist/media/image-endpoint.mjs +6 -4
  497. package/dist/media/image-endpoint.mjs.map +1 -1
  498. package/dist/media/index.d.mts +1 -1
  499. package/dist/media/index.mjs +13 -3
  500. package/dist/media/index.mjs.map +1 -1
  501. package/dist/media/local-runtime.d.mts +14 -12
  502. package/dist/media/local-runtime.d.mts.map +1 -1
  503. package/dist/media/local-runtime.mjs +12 -6
  504. package/dist/media/local-runtime.mjs.map +1 -1
  505. package/dist/{media-CzhAn2--.mjs → media-BBm8Wg3f.mjs} +69 -12
  506. package/dist/media-BBm8Wg3f.mjs.map +1 -0
  507. package/dist/{media-BWWWb8kg.mjs → media-BHjus86Y.mjs} +133 -9
  508. package/dist/media-BHjus86Y.mjs.map +1 -0
  509. package/dist/{media-allowlist-D6x-Us02.mjs → media-allowlist-CoTBWoDp.mjs} +3 -2
  510. package/dist/media-allowlist-CoTBWoDp.mjs.map +1 -0
  511. package/dist/media-folders-BGkATs8n.mjs +210 -0
  512. package/dist/media-folders-BGkATs8n.mjs.map +1 -0
  513. package/dist/{media-upload-CHBVn6m-.mjs → media-upload-BCidR-EY.mjs} +9 -9
  514. package/dist/{media-upload-CHBVn6m-.mjs.map → media-upload-BCidR-EY.mjs.map} +1 -1
  515. package/dist/{media-url-BvoX88FA.mjs → media-url-DHoPr0hq.mjs} +1 -1
  516. package/dist/{media-url-BvoX88FA.mjs.map → media-url-DHoPr0hq.mjs.map} +1 -1
  517. package/dist/media-usage-CdY5jhSR.mjs +1833 -0
  518. package/dist/media-usage-CdY5jhSR.mjs.map +1 -0
  519. package/dist/media-usage-activation-BgeMtK5C.mjs +78 -0
  520. package/dist/media-usage-activation-BgeMtK5C.mjs.map +1 -0
  521. package/dist/{media-usage-work-CLo_6tct.mjs → media-usage-work-CnGCdHxx.mjs} +4 -4
  522. package/dist/{media-usage-work-CLo_6tct.mjs.map → media-usage-work-CnGCdHxx.mjs.map} +1 -1
  523. package/dist/{media-usage-work-DrCbqBcl.mjs → media-usage-work-D6D9sMCA.mjs} +221 -59
  524. package/dist/media-usage-work-D6D9sMCA.mjs.map +1 -0
  525. package/dist/{media-usage-write-fence-aHEgN1zB.mjs → media-usage-write-fence-hjeD4u7s.mjs} +2 -2
  526. package/dist/{media-usage-write-fence-aHEgN1zB.mjs.map → media-usage-write-fence-hjeD4u7s.mjs.map} +1 -1
  527. package/dist/{menus-DSo5QeW0.mjs → menus-6qSwTNxG.mjs} +375 -107
  528. package/dist/menus-6qSwTNxG.mjs.map +1 -0
  529. package/dist/{menus-TCNSd2vp.mjs → menus-uvPLXMZf.mjs} +4 -4
  530. package/dist/{menus-TCNSd2vp.mjs.map → menus-uvPLXMZf.mjs.map} +1 -1
  531. package/dist/migrations/index.d.mts +1 -1
  532. package/dist/migrations/index.mjs +3 -3
  533. package/dist/{mime-DfIVjNkr.mjs → mime-CUIG6x84.mjs} +1 -1
  534. package/dist/mime-CUIG6x84.mjs.map +1 -0
  535. package/dist/{mode-BDoH_WcF.mjs → mode-DN_WneRx.mjs} +1 -1
  536. package/dist/{mode-BDoH_WcF.mjs.map → mode-DN_WneRx.mjs.map} +1 -1
  537. package/dist/node-sqlite-compat-BuJXIeMz.mjs +46 -0
  538. package/dist/node-sqlite-compat-BuJXIeMz.mjs.map +1 -0
  539. package/dist/{normalize-Bc-s_r-a.mjs → normalize-C2xNjTSG.mjs} +36 -5
  540. package/dist/normalize-C2xNjTSG.mjs.map +1 -0
  541. package/dist/{oauth-authorization-iOikmrJd.mjs → oauth-authorization-Dj6Kwc21.mjs} +5 -5
  542. package/dist/{oauth-authorization-iOikmrJd.mjs.map → oauth-authorization-Dj6Kwc21.mjs.map} +1 -1
  543. package/dist/{oauth-clients-CvI0nHMJ.mjs → oauth-clients-FD7I0J8m.mjs} +1 -1
  544. package/dist/{oauth-clients-CvI0nHMJ.mjs.map → oauth-clients-FD7I0J8m.mjs.map} +1 -1
  545. package/dist/{oauth-state-store-Ba8KZlwW.mjs → oauth-state-store-DkmDOmHs.mjs} +1 -1
  546. package/dist/{oauth-state-store-Ba8KZlwW.mjs.map → oauth-state-store-DkmDOmHs.mjs.map} +1 -1
  547. package/dist/{oauth-user-lookup-C5SQwGYC.mjs → oauth-user-lookup-CvIwGpYd.mjs} +1 -1
  548. package/dist/{oauth-user-lookup-C5SQwGYC.mjs.map → oauth-user-lookup-CvIwGpYd.mjs.map} +1 -1
  549. package/dist/object-cache/memory.d.mts +1 -1
  550. package/dist/{object-cache-71tPfV4A.mjs → object-cache-DVKnyOfy.mjs} +19 -3
  551. package/dist/object-cache-DVKnyOfy.mjs.map +1 -0
  552. package/dist/{options-CBXIHUoT.mjs → options-BHPeIbBd.mjs} +1 -1
  553. package/dist/{options-CBXIHUoT.mjs.map → options-BHPeIbBd.mjs.map} +1 -1
  554. package/dist/{options--hhz-jZ1.d.mts → options-CYb4y7hM.d.mts} +24 -11
  555. package/dist/options-CYb4y7hM.d.mts.map +1 -0
  556. package/dist/page/index.d.mts +2 -2
  557. package/dist/{parse-DFbuc-Fh.mjs → parse-Ckpg-Qfp.mjs} +2 -2
  558. package/dist/{parse-DFbuc-Fh.mjs.map → parse-Ckpg-Qfp.mjs.map} +1 -1
  559. package/dist/{passkey-config-DFAmUyuE.mjs → passkey-config-O_lj3y0k.mjs} +1 -1
  560. package/dist/{passkey-config-DFAmUyuE.mjs.map → passkey-config-O_lj3y0k.mjs.map} +1 -1
  561. package/dist/{patterns-CiyXeDgr.mjs → patterns-D9iDO3j8.mjs} +1 -1
  562. package/dist/{patterns-CiyXeDgr.mjs.map → patterns-D9iDO3j8.mjs.map} +1 -1
  563. package/dist/{placeholder-Dp0K3RkD.d.mts → placeholder-4X9BrRrp.d.mts} +7 -1
  564. package/dist/placeholder-4X9BrRrp.d.mts.map +1 -0
  565. package/dist/plugin-types.d.mts +8 -3
  566. package/dist/plugin-types.d.mts.map +1 -1
  567. package/dist/plugin-utils.d.mts +10 -10
  568. package/dist/plugins/adapt-sandbox-entry.d.mts +10 -10
  569. package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
  570. package/dist/{policy-TluUIxuu.mjs → policy-Dhtwntrt.mjs} +2 -2
  571. package/dist/{policy-TluUIxuu.mjs.map → policy-Dhtwntrt.mjs.map} +1 -1
  572. package/dist/{portable-text-imBW5cHK.mjs → portable-text-U85hOafW.mjs} +1 -1
  573. package/dist/{portable-text-imBW5cHK.mjs.map → portable-text-U85hOafW.mjs.map} +1 -1
  574. package/dist/{preview-PW-nOnq4.mjs → preview-B_vJLvf8.mjs} +2 -2
  575. package/dist/{preview-PW-nOnq4.mjs.map → preview-B_vJLvf8.mjs.map} +1 -1
  576. package/dist/{protocol-DjsJHTlK.d.mts → protocol-JdD2YhY9.d.mts} +2 -2
  577. package/dist/{protocol-DjsJHTlK.d.mts.map → protocol-JdD2YhY9.d.mts.map} +1 -1
  578. package/dist/{public-url-CWoFhQT8.mjs → public-url-CarhZJgf.mjs} +1 -1
  579. package/dist/{public-url-CWoFhQT8.mjs.map → public-url-CarhZJgf.mjs.map} +1 -1
  580. package/dist/{query-zTVAR1Sl.mjs → query-BZKZcG5b.mjs} +15 -15
  581. package/dist/{query-zTVAR1Sl.mjs.map → query-BZKZcG5b.mjs.map} +1 -1
  582. package/dist/{query-BWs7O5Il.mjs → query-BndaFxxp.mjs} +5 -5
  583. package/dist/{query-BWs7O5Il.mjs.map → query-BndaFxxp.mjs.map} +1 -1
  584. package/dist/{rate-limit-Bx4g9m3p.mjs → rate-limit-BZmATC6W.mjs} +2 -2
  585. package/dist/{rate-limit-Bx4g9m3p.mjs.map → rate-limit-BZmATC6W.mjs.map} +1 -1
  586. package/dist/{redirect-COsS6SWJ.mjs → redirect-CZkbozu9.mjs} +2 -2
  587. package/dist/{redirect-COsS6SWJ.mjs.map → redirect-CZkbozu9.mjs.map} +1 -1
  588. package/dist/{redirect-BgAOGHDq.mjs → redirect-CzqnLxK-.mjs} +1 -1
  589. package/dist/{redirect-BgAOGHDq.mjs.map → redirect-CzqnLxK-.mjs.map} +1 -1
  590. package/dist/{redirects-DTDHWH31.mjs → redirects-BLNOWQTn.mjs} +64 -29
  591. package/dist/redirects-BLNOWQTn.mjs.map +1 -0
  592. package/dist/{redirects-DyChwDkG.mjs → redirects-CqzNM0KM.mjs} +5 -5
  593. package/dist/{redirects-DyChwDkG.mjs.map → redirects-CqzNM0KM.mjs.map} +1 -1
  594. package/dist/{registry-Cqa2I749.mjs → registry-AZAsRsor.mjs} +30 -126
  595. package/dist/registry-AZAsRsor.mjs.map +1 -0
  596. package/dist/{relation-BnHB0T4J.mjs → relation-04DfKRTO.mjs} +2 -2
  597. package/dist/{relation-BnHB0T4J.mjs.map → relation-04DfKRTO.mjs.map} +1 -1
  598. package/dist/{relations-BBApI4e2.mjs → relations-C4htk5sb.mjs} +7 -7
  599. package/dist/relations-C4htk5sb.mjs.map +1 -0
  600. package/dist/{relations-D5sM5fW0.mjs → relations-CcFwDJdJ.mjs} +4 -4
  601. package/dist/{relations-D5sM5fW0.mjs.map → relations-CcFwDJdJ.mjs.map} +1 -1
  602. package/dist/{relations-BHo5A8Zf.d.mts → relations-DnRK4YIT.d.mts} +347 -54
  603. package/dist/relations-DnRK4YIT.d.mts.map +1 -0
  604. package/dist/{request-cache-BSUptuJR.mjs → request-cache-DH9zdCOD.mjs} +1 -1
  605. package/dist/{request-cache-BSUptuJR.mjs.map → request-cache-DH9zdCOD.mjs.map} +1 -1
  606. package/dist/request-context.d.mts +1 -1
  607. package/dist/{request-meta-DgZJh3zG.mjs → request-meta-DYxLlnmz.mjs} +2 -2
  608. package/dist/{request-meta-DgZJh3zG.mjs.map → request-meta-DYxLlnmz.mjs.map} +1 -1
  609. package/dist/{resolve-DOhVEvty.mjs → resolve-prZ3mP7l.mjs} +1 -1
  610. package/dist/{resolve-DOhVEvty.mjs.map → resolve-prZ3mP7l.mjs.map} +1 -1
  611. package/dist/{runner-C1T11CfC.mjs → runner-BLIDKpmA.mjs} +471 -290
  612. package/dist/runner-BLIDKpmA.mjs.map +1 -0
  613. package/dist/{runner-BYHZn3Jy.d.mts → runner-YKfIMnaF.d.mts} +3 -2
  614. package/dist/runner-YKfIMnaF.d.mts.map +1 -0
  615. package/dist/runtime.d.mts +11 -11
  616. package/dist/runtime.mjs +1 -1
  617. package/dist/{schema-DZ5YVVZj.mjs → schema-CGnBeLiW.mjs} +3 -3
  618. package/dist/{schema-DZ5YVVZj.mjs.map → schema-CGnBeLiW.mjs.map} +1 -1
  619. package/dist/{schema-2AOsSn2v.mjs → schema-DYr-Vd-L.mjs} +36 -6
  620. package/dist/{schema-2AOsSn2v.mjs.map → schema-DYr-Vd-L.mjs.map} +1 -1
  621. package/dist/{scopes-8ClPF2bC.mjs → scopes-BhWvBavj.mjs} +3 -3
  622. package/dist/{scopes-8ClPF2bC.mjs.map → scopes-BhWvBavj.mjs.map} +1 -1
  623. package/dist/{search-u1QYdOCG.mjs → search-C-qDk4kx.mjs} +4 -4
  624. package/dist/{search-u1QYdOCG.mjs.map → search-C-qDk4kx.mjs.map} +1 -1
  625. package/dist/{secrets-BEgsAQEU.mjs → secrets-rEtHt-6z.mjs} +3 -3
  626. package/dist/{secrets-BEgsAQEU.mjs.map → secrets-rEtHt-6z.mjs.map} +1 -1
  627. package/dist/{sections-DmHviGyM.mjs → sections-B_nYvlwn.mjs} +2 -2
  628. package/dist/{sections-DmHviGyM.mjs.map → sections-B_nYvlwn.mjs.map} +1 -1
  629. package/dist/seed/index.d.mts +2 -2
  630. package/dist/seed/index.mjs +25 -23
  631. package/dist/seo/index.d.mts +3 -3
  632. package/dist/seo/index.mjs +17 -15
  633. package/dist/seo/index.mjs.map +1 -1
  634. package/dist/{seo-Dq4G-RrO.mjs → seo-926NRIgt.mjs} +3 -3
  635. package/dist/{seo-Dq4G-RrO.mjs.map → seo-926NRIgt.mjs.map} +1 -1
  636. package/dist/{seo-AFpVvTqr.mjs → seo-CVWkdv6x.mjs} +1 -1
  637. package/dist/{seo-AFpVvTqr.mjs.map → seo-CVWkdv6x.mjs.map} +1 -1
  638. package/dist/{service-BpArayPr.mjs → service-CJl3ZAYs.mjs} +3 -3
  639. package/dist/{service-BpArayPr.mjs.map → service-CJl3ZAYs.mjs.map} +1 -1
  640. package/dist/{session-user-BEorKVT-.mjs → session-user-BlMgqCHS.mjs} +1 -1
  641. package/dist/{session-user-BEorKVT-.mjs.map → session-user-BlMgqCHS.mjs.map} +1 -1
  642. package/dist/{settings-xRdoUZr0.mjs → settings--wobrkkC.mjs} +21 -8
  643. package/dist/settings--wobrkkC.mjs.map +1 -0
  644. package/dist/{settings-DE9xtzqd.mjs → settings-Cwm6o2jY.mjs} +3 -3
  645. package/dist/{settings-DE9xtzqd.mjs.map → settings-Cwm6o2jY.mjs.map} +1 -1
  646. package/dist/{setup-complete-BnT8pq_C.mjs → setup-complete-DQbClIN2.mjs} +2 -2
  647. package/dist/{setup-complete-BnT8pq_C.mjs.map → setup-complete-DQbClIN2.mjs.map} +1 -1
  648. package/dist/{setup-nonce-CBitf_oQ.mjs → setup-nonce-DZCFS2sn.mjs} +1 -1
  649. package/dist/{setup-nonce-CBitf_oQ.mjs.map → setup-nonce-DZCFS2sn.mjs.map} +1 -1
  650. package/dist/{single-flight-cache-B0eLKS6K.mjs → single-flight-cache-BPuGPdcN.mjs} +2 -2
  651. package/dist/{single-flight-cache-B0eLKS6K.mjs.map → single-flight-cache-BPuGPdcN.mjs.map} +1 -1
  652. package/dist/{site-url-DjpIXMXJ.mjs → site-url-BrxA_E9H.mjs} +3 -3
  653. package/dist/{site-url-DjpIXMXJ.mjs.map → site-url-BrxA_E9H.mjs.map} +1 -1
  654. package/dist/{snapshot-BBlGkb2q.mjs → snapshot--OCDTqdb.mjs} +1 -1
  655. package/dist/{snapshot-BBlGkb2q.mjs.map → snapshot--OCDTqdb.mjs.map} +1 -1
  656. package/dist/{ssrf-Cpvg_n3A.mjs → ssrf-5sIM5jzl.mjs} +23 -6
  657. package/dist/{ssrf-Cpvg_n3A.mjs.map → ssrf-5sIM5jzl.mjs.map} +1 -1
  658. package/dist/{state-DYwPue40.mjs → state-BpFVsJZB.mjs} +12 -4
  659. package/dist/state-BpFVsJZB.mjs.map +1 -0
  660. package/dist/{status-CWVYpbD8.mjs → status-B777-dXz.mjs} +1 -1
  661. package/dist/{status-CWVYpbD8.mjs.map → status-B777-dXz.mjs.map} +1 -1
  662. package/dist/storage/local.d.mts +2 -1
  663. package/dist/storage/local.d.mts.map +1 -1
  664. package/dist/storage/local.mjs +1 -1
  665. package/dist/storage/local.mjs.map +1 -1
  666. package/dist/storage/s3.d.mts +2 -1
  667. package/dist/storage/s3.d.mts.map +1 -1
  668. package/dist/storage/s3.mjs +3 -2
  669. package/dist/storage/s3.mjs.map +1 -1
  670. package/dist/{target-DixqqV5i.mjs → target-BmR0iTwz.mjs} +1 -1
  671. package/dist/{target-DixqqV5i.mjs.map → target-BmR0iTwz.mjs.map} +1 -1
  672. package/dist/{taxonomies-OHUIiIpI.mjs → taxonomies-DkAkMupr.mjs} +17 -14
  673. package/dist/taxonomies-DkAkMupr.mjs.map +1 -0
  674. package/dist/{taxonomies-BlrncHi9.mjs → taxonomies-Wfy_Lg12.mjs} +43 -17
  675. package/dist/taxonomies-Wfy_Lg12.mjs.map +1 -0
  676. package/dist/{taxonomy-BqCZ1TdU.mjs → taxonomy-tJepplLL.mjs} +5 -5
  677. package/dist/{taxonomy-BqCZ1TdU.mjs.map → taxonomy-tJepplLL.mjs.map} +1 -1
  678. package/dist/{tokens-C39klX8R.mjs → tokens-BCO1oruF.mjs} +1 -1
  679. package/dist/{tokens-C39klX8R.mjs.map → tokens-BCO1oruF.mjs.map} +1 -1
  680. package/dist/{transaction-NQj4VJ7Z.mjs → transaction-0KOpOyMu.mjs} +2 -1
  681. package/dist/{transaction-NQj4VJ7Z.mjs.map → transaction-0KOpOyMu.mjs.map} +1 -1
  682. package/dist/{transport-DBJwa-Si.mjs → transport-Dr3qtwXC.mjs} +4 -3
  683. package/dist/{transport-DBJwa-Si.mjs.map → transport-Dr3qtwXC.mjs.map} +1 -1
  684. package/dist/{transport-RQE36APG.d.mts → transport-xh6YKW2G.d.mts} +1 -1
  685. package/dist/{transport-RQE36APG.d.mts.map → transport-xh6YKW2G.d.mts.map} +1 -1
  686. package/dist/{trusted-proxy-CEVekpby.mjs → trusted-proxy-BR5F1Otp.mjs} +1 -1
  687. package/dist/{trusted-proxy-CEVekpby.mjs.map → trusted-proxy-BR5F1Otp.mjs.map} +1 -1
  688. package/dist/{types-BW1syrlw.mjs → types-3qNFPlci.mjs} +1 -1
  689. package/dist/{types-BW1syrlw.mjs.map → types-3qNFPlci.mjs.map} +1 -1
  690. package/dist/types-75KIqzL9.mjs.map +1 -1
  691. package/dist/{types-DrJrVCT1.d.mts → types-B6GrPic4.d.mts} +9 -3
  692. package/dist/{types-DrJrVCT1.d.mts.map → types-B6GrPic4.d.mts.map} +1 -1
  693. package/dist/{types-DN64_zzZ.d.mts → types-BuHW4xDE.d.mts} +11 -10
  694. package/dist/types-BuHW4xDE.d.mts.map +1 -0
  695. package/dist/{types-DWRhk9xZ.d.mts → types-C00oyCZp.d.mts} +1 -1
  696. package/dist/{types-DWRhk9xZ.d.mts.map → types-C00oyCZp.d.mts.map} +1 -1
  697. package/dist/{types-BntycPTF.d.mts → types-CDtkNSFQ.d.mts} +1 -1
  698. package/dist/{types-BntycPTF.d.mts.map → types-CDtkNSFQ.d.mts.map} +1 -1
  699. package/dist/{types-Dfk8KI5R.d.mts → types-CPeXo1wF.d.mts} +1 -1
  700. package/dist/{types-Dfk8KI5R.d.mts.map → types-CPeXo1wF.d.mts.map} +1 -1
  701. package/dist/types-CQPWhPxE.mjs.map +1 -1
  702. package/dist/{types-BcqdcT-r.d.mts → types-CXFkwEGC.d.mts} +3 -2
  703. package/dist/types-CXFkwEGC.d.mts.map +1 -0
  704. package/dist/{types-CtzytVXn.mjs → types-DM3vioBY.mjs} +1 -1
  705. package/dist/{types-CtzytVXn.mjs.map → types-DM3vioBY.mjs.map} +1 -1
  706. package/dist/{types-CXnOzDe1.d.mts → types-DczELMop.d.mts} +1 -1
  707. package/dist/{types-CXnOzDe1.d.mts.map → types-DczELMop.d.mts.map} +1 -1
  708. package/dist/types-DfuTR6dL.mjs +3 -0
  709. package/dist/{types-DQIDRKF2.d.mts → types-FV23hEoW.d.mts} +10 -1
  710. package/dist/types-FV23hEoW.d.mts.map +1 -0
  711. package/dist/{types-C2pMvO62.d.mts → types-xDMtUFnO.d.mts} +2 -1
  712. package/dist/types-xDMtUFnO.d.mts.map +1 -0
  713. package/dist/{upload-attempts-D4FS8LT6.mjs → upload-attempts-BsqoQ1fn.mjs} +1 -1
  714. package/dist/{upload-attempts-D4FS8LT6.mjs.map → upload-attempts-BsqoQ1fn.mjs.map} +1 -1
  715. package/dist/{url-pattern-BHS0V5Ng.mjs → url-pattern-D4c2czpJ.mjs} +1 -1
  716. package/dist/{url-pattern-BHS0V5Ng.mjs.map → url-pattern-D4c2czpJ.mjs.map} +1 -1
  717. package/dist/{user-DJZfUJVE.mjs → user-BTZ40aq6.mjs} +2 -2
  718. package/dist/{user-DJZfUJVE.mjs.map → user-BTZ40aq6.mjs.map} +1 -1
  719. package/dist/{utils-D4vD_Y98.mjs → utils-BKvNjjhY.mjs} +1 -1
  720. package/dist/{utils-D4vD_Y98.mjs.map → utils-BKvNjjhY.mjs.map} +1 -1
  721. package/dist/{validate-BMLBvxOC.mjs → validate-DE_LRwo9.mjs} +2 -2
  722. package/dist/{validate-BMLBvxOC.mjs.map → validate-DE_LRwo9.mjs.map} +1 -1
  723. package/dist/{validate-CTqt5nuE.d.mts → validate-DUP27OYD.d.mts} +5 -5
  724. package/dist/{validate-CTqt5nuE.d.mts.map → validate-DUP27OYD.d.mts.map} +1 -1
  725. package/dist/{validation-npvENUCB.mjs → validation-BMImFJ11.mjs} +10 -8
  726. package/dist/{validation-npvENUCB.mjs.map → validation-BMImFJ11.mjs.map} +1 -1
  727. package/dist/version-Bii3suaO.mjs +7 -0
  728. package/dist/{version-DB_fqCSI.mjs.map → version-Bii3suaO.mjs.map} +1 -1
  729. package/dist/{widgets-BD3ho7Rq.mjs → widgets-D3gJ10xU.mjs} +18 -5
  730. package/dist/widgets-D3gJ10xU.mjs.map +1 -0
  731. package/dist/{wxr-taxonomies-Ca68766D.d.mts → wxr-taxonomies-B2Og_6rU.d.mts} +1 -1
  732. package/dist/{wxr-taxonomies-Ca68766D.d.mts.map → wxr-taxonomies-B2Og_6rU.d.mts.map} +1 -1
  733. package/dist/{wxr-taxonomies-B1R3vBoD.mjs → wxr-taxonomies-Cxngsjwo.mjs} +4 -4
  734. package/dist/{wxr-taxonomies-B1R3vBoD.mjs.map → wxr-taxonomies-Cxngsjwo.mjs.map} +1 -1
  735. package/dist/{zod-generator-LC-on_Vb.mjs → zod-generator-BuYSQJB7.mjs} +32 -22
  736. package/dist/zod-generator-BuYSQJB7.mjs.map +1 -0
  737. package/package.json +20 -9
  738. package/src/api/errors.ts +22 -0
  739. package/src/api/handlers/content.ts +49 -11
  740. package/src/api/handlers/index.ts +18 -0
  741. package/src/api/handlers/manifest.ts +161 -12
  742. package/src/api/handlers/media-allowlist.ts +1 -0
  743. package/src/api/handlers/media-folders.ts +130 -0
  744. package/src/api/handlers/media-usage-activation.ts +102 -0
  745. package/src/api/handlers/media-usage.ts +108 -0
  746. package/src/api/handlers/media.ts +92 -2
  747. package/src/api/handlers/registry.ts +483 -561
  748. package/src/api/handlers/taxonomies.ts +16 -9
  749. package/src/api/openapi/document.ts +277 -7
  750. package/src/api/parse.ts +1 -1
  751. package/src/api/schemas/auth.ts +4 -4
  752. package/src/api/schemas/bylines.ts +3 -3
  753. package/src/api/schemas/comments.ts +1 -1
  754. package/src/api/schemas/common.ts +3 -2
  755. package/src/api/schemas/content.ts +78 -22
  756. package/src/api/schemas/media-usage.ts +77 -0
  757. package/src/api/schemas/media.ts +121 -0
  758. package/src/api/schemas/redirects.ts +6 -1
  759. package/src/api/schemas/schema.ts +4 -0
  760. package/src/api/schemas/setup.ts +1 -1
  761. package/src/api/schemas/taxonomies.ts +2 -2
  762. package/src/api/schemas/users.ts +1 -1
  763. package/src/api/types.ts +23 -10
  764. package/src/astro/dev-typegen.ts +35 -0
  765. package/src/astro/image-endpoint.ts +2 -2
  766. package/src/astro/integration/dev-typegen.ts +91 -0
  767. package/src/astro/integration/index.ts +9 -44
  768. package/src/astro/integration/routes.ts +23 -0
  769. package/src/astro/integration/vite-config.ts +23 -9
  770. package/src/astro/middleware/auth.ts +4 -0
  771. package/src/astro/middleware.ts +5 -7
  772. package/src/astro/routes/api/admin/api-tokens/index.ts +4 -1
  773. package/src/astro/routes/api/admin/media-usage/activation.ts +42 -0
  774. package/src/astro/routes/api/admin/media-usage/progress.ts +37 -0
  775. package/src/astro/routes/api/admin/oauth-clients/[id].ts +1 -1
  776. package/src/astro/routes/api/admin/oauth-clients/index.ts +1 -1
  777. package/src/astro/routes/api/admin/plugins/registry/[id]/update.ts +4 -0
  778. package/src/astro/routes/api/admin/plugins/registry/artifact.ts +247 -101
  779. package/src/astro/routes/api/admin/plugins/registry/install.ts +4 -0
  780. package/src/astro/routes/api/admin/plugins/registry/verify.ts +60 -0
  781. package/src/astro/routes/api/auth/me.ts +14 -2
  782. package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +8 -2
  783. package/src/astro/routes/api/content/[collection]/[id]/publish.ts +2 -1
  784. package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +6 -2
  785. package/src/astro/routes/api/manifest.ts +1 -1
  786. package/src/astro/routes/api/media/[id]/replace.ts +114 -0
  787. package/src/astro/routes/api/media/[id].ts +3 -0
  788. package/src/astro/routes/api/media/file/[...key].ts +4 -1
  789. package/src/astro/routes/api/media/folders/[id].ts +55 -0
  790. package/src/astro/routes/api/media/folders/index.ts +37 -0
  791. package/src/astro/routes/api/media/upload-url.ts +8 -4
  792. package/src/astro/routes/api/media.ts +66 -10
  793. package/src/astro/routes/api/menus/[name]/items/[id].ts +7 -2
  794. package/src/astro/routes/api/menus/[name]/items.ts +4 -1
  795. package/src/astro/routes/api/menus/[name]/reorder.ts +4 -1
  796. package/src/astro/routes/api/menus/[name]/translations.ts +4 -1
  797. package/src/astro/routes/api/menus/[name].ts +7 -2
  798. package/src/astro/routes/api/menus/index.ts +4 -1
  799. package/src/astro/routes/api/settings/email.ts +1 -1
  800. package/src/astro/routes/api/settings.ts +4 -1
  801. package/src/astro/routes/api/setup/dev-bypass.ts +12 -0
  802. package/src/astro/routes/api/taxonomies/[name]/reorder.ts +4 -1
  803. package/src/astro/routes/api/taxonomies/[name]/terms/[slug]/translations.ts +4 -1
  804. package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +7 -2
  805. package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +4 -1
  806. package/src/astro/routes/api/taxonomies/[name].ts +7 -2
  807. package/src/astro/routes/api/taxonomies/index.ts +4 -1
  808. package/src/astro/routes/api/widget-areas/[name]/reorder.ts +3 -1
  809. package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +5 -2
  810. package/src/astro/routes/api/widget-areas/[name]/widgets.ts +3 -1
  811. package/src/astro/routes/api/widget-areas/[name].ts +3 -1
  812. package/src/astro/routes/api/widget-areas/index.ts +3 -1
  813. package/src/astro/types.ts +34 -6
  814. package/src/auth/challenge-store.ts +3 -1
  815. package/src/aws-sdk.d.ts +1 -0
  816. package/src/cache/chrome-tags.ts +24 -0
  817. package/src/client/index.ts +140 -2
  818. package/src/client/transport.ts +6 -2
  819. package/src/components/EmDashImage.astro +156 -51
  820. package/src/components/EmDashMedia.astro +2 -1
  821. package/src/components/Gallery.astro +7 -0
  822. package/src/components/InlinePortableTextEditor.tsx +136 -2
  823. package/src/components/LiveSearch.astro +23 -0
  824. package/src/components/inline-code-block.tsx +180 -49
  825. package/src/components/widgets/Archives.astro +2 -37
  826. package/src/content/converters/gallery.ts +2 -0
  827. package/src/content/converters/index.ts +2 -0
  828. package/src/content/converters/mark-safety.ts +1 -0
  829. package/src/content/converters/portable-text-identity.ts +139 -0
  830. package/src/content/converters/portable-text-to-prosemirror.ts +202 -69
  831. package/src/content/converters/prosemirror-to-portable-text.ts +96 -25
  832. package/src/content/converters/types.ts +3 -0
  833. package/src/database/connection.ts +2 -31
  834. package/src/database/index.ts +1 -1
  835. package/src/database/json-recordset.ts +16 -0
  836. package/src/database/migrations/017_authorization_codes.ts +14 -4
  837. package/src/database/migrations/019_i18n.ts +1 -1
  838. package/src/database/migrations/071_restore_content_bylines_table.ts +43 -0
  839. package/src/database/migrations/072_media_folders.ts +72 -0
  840. package/src/database/migrations/073_media_focal_point.ts +57 -0
  841. package/src/database/migrations/074_content_deleted_scheduled_index.ts +55 -0
  842. package/src/database/migrations/runner.ts +11 -3
  843. package/src/database/repositories/byline.ts +32 -16
  844. package/src/database/repositories/content.ts +119 -51
  845. package/src/database/repositories/media-folders.ts +115 -0
  846. package/src/database/repositories/media-usage-work.ts +395 -111
  847. package/src/database/repositories/media-usage.ts +1104 -47
  848. package/src/database/repositories/media.ts +156 -29
  849. package/src/database/repositories/plugin-storage.ts +74 -9
  850. package/src/database/repositories/types.ts +8 -7
  851. package/src/database/transaction.ts +1 -0
  852. package/src/database/types.ts +13 -0
  853. package/src/db/adapters.ts +10 -3
  854. package/src/db/node-sqlite-compat.ts +78 -0
  855. package/src/db/postgres.ts +23 -0
  856. package/src/db/sqlite.ts +3 -3
  857. package/src/emdash-runtime.ts +186 -251
  858. package/src/fields/boolean.ts +1 -1
  859. package/src/fields/datetime.ts +1 -1
  860. package/src/fields/file.ts +1 -1
  861. package/src/fields/image.ts +7 -1
  862. package/src/fields/integer.ts +1 -1
  863. package/src/fields/json.ts +1 -1
  864. package/src/fields/multiselect.ts +1 -1
  865. package/src/fields/number.ts +1 -1
  866. package/src/fields/portable-text.ts +1 -1
  867. package/src/fields/richtext.ts +1 -1
  868. package/src/fields/select.ts +1 -1
  869. package/src/fields/slug.ts +1 -1
  870. package/src/fields/text.ts +1 -1
  871. package/src/fields/textarea.ts +1 -1
  872. package/src/fields/types.ts +8 -4
  873. package/src/index.ts +30 -3
  874. package/src/loader.ts +63 -10
  875. package/src/mcp/server.ts +228 -24
  876. package/src/media/focal-point.ts +37 -0
  877. package/src/media/image-endpoint.ts +5 -2
  878. package/src/media/local-runtime.ts +8 -0
  879. package/src/media/mime.ts +1 -0
  880. package/src/media/normalize.ts +25 -4
  881. package/src/media/types.ts +10 -0
  882. package/src/media/usage/activation.ts +162 -35
  883. package/src/media/usage/capture-triggers.ts +19 -3
  884. package/src/media/usage/cleanup.ts +9 -8
  885. package/src/media/usage/collection-deletion-processor.ts +7 -3
  886. package/src/media/usage/collection-deletion.ts +10 -0
  887. package/src/media/usage/content-refresh.ts +258 -39
  888. package/src/media/usage/content-snapshots.ts +128 -31
  889. package/src/media/usage/extractor.ts +32 -13
  890. package/src/media/usage/maintenance-engine.ts +166 -0
  891. package/src/media/usage/reconciliation-processor.ts +106 -29
  892. package/src/media/usage/reconciliation.ts +90 -7
  893. package/src/media/usage/work-processor.ts +342 -101
  894. package/src/menus/index.ts +16 -0
  895. package/src/object-cache/index.ts +44 -2
  896. package/src/plugin-types.ts +18 -1
  897. package/src/plugins/hooks.ts +4 -1
  898. package/src/plugins/index.ts +7 -0
  899. package/src/plugins/manager.ts +2 -1
  900. package/src/plugins/manifest-schema.ts +1 -1
  901. package/src/plugins/routes.ts +3 -1
  902. package/src/plugins/sandbox/hook-result.ts +52 -0
  903. package/src/plugins/sandbox/index.ts +10 -0
  904. package/src/plugins/save-rejection.ts +20 -0
  905. package/src/plugins/scheduler/node.ts +1 -13
  906. package/src/plugins/scheduler/types.ts +0 -2
  907. package/src/plugins/state.ts +13 -3
  908. package/src/plugins/types.ts +6 -0
  909. package/src/registry/artifact-checksum.ts +15 -0
  910. package/src/registry/artifact-fetch.ts +530 -0
  911. package/src/registry/artifact-verification.ts +85 -0
  912. package/src/registry/authoritative-records.ts +180 -0
  913. package/src/registry/config.ts +24 -6
  914. package/src/registry/record-labels.ts +28 -0
  915. package/src/registry/types.ts +12 -15
  916. package/src/schema/registry.ts +26 -3
  917. package/src/schema/types.ts +1 -0
  918. package/src/schema/zod-generator.ts +28 -17
  919. package/src/security/ssrf.ts +26 -4
  920. package/src/seed/apply.ts +4 -4
  921. package/src/settings/index.ts +16 -0
  922. package/src/storage/local.ts +1 -0
  923. package/src/storage/s3.ts +2 -0
  924. package/src/storage/types.ts +2 -0
  925. package/src/taxonomies/index.ts +40 -20
  926. package/src/ui.ts +2 -0
  927. package/src/widgets/archives.ts +67 -0
  928. package/src/widgets/index.ts +16 -0
  929. package/dist/api-BeBglVxU.mjs +0 -4695
  930. package/dist/api-BeBglVxU.mjs.map +0 -1
  931. package/dist/byline-yjvWq4ip.mjs.map +0 -1
  932. package/dist/challenge-store-SIL5DsF2.mjs.map +0 -1
  933. package/dist/collection-deletion-DQPvMD8R.mjs.map +0 -1
  934. package/dist/content-ZHCc1ngN.mjs.map +0 -1
  935. package/dist/content-refresh-B_8SJzuU.mjs.map +0 -1
  936. package/dist/context-ZldbZcle.mjs.map +0 -1
  937. package/dist/errors-CLUPm7aD.mjs.map +0 -1
  938. package/dist/index-BVBZj4-7.d.mts.map +0 -1
  939. package/dist/loader-CdG_Ci3H.mjs.map +0 -1
  940. package/dist/manifest-schema-BXbUAHod.mjs.map +0 -1
  941. package/dist/media-BWWWb8kg.mjs.map +0 -1
  942. package/dist/media-CzhAn2--.mjs.map +0 -1
  943. package/dist/media-allowlist-D6x-Us02.mjs.map +0 -1
  944. package/dist/media-usage-BZCydfvu.mjs.map +0 -1
  945. package/dist/media-usage-work-DrCbqBcl.mjs.map +0 -1
  946. package/dist/menus-DSo5QeW0.mjs.map +0 -1
  947. package/dist/mime-DfIVjNkr.mjs.map +0 -1
  948. package/dist/normalize-Bc-s_r-a.mjs.map +0 -1
  949. package/dist/object-cache-71tPfV4A.mjs.map +0 -1
  950. package/dist/options--hhz-jZ1.d.mts.map +0 -1
  951. package/dist/placeholder-Dp0K3RkD.d.mts.map +0 -1
  952. package/dist/redirects-DTDHWH31.mjs.map +0 -1
  953. package/dist/registry-Cqa2I749.mjs.map +0 -1
  954. package/dist/relations-BBApI4e2.mjs.map +0 -1
  955. package/dist/relations-BHo5A8Zf.d.mts.map +0 -1
  956. package/dist/runner-BYHZn3Jy.d.mts.map +0 -1
  957. package/dist/runner-C1T11CfC.mjs.map +0 -1
  958. package/dist/settings-xRdoUZr0.mjs.map +0 -1
  959. package/dist/state-DYwPue40.mjs.map +0 -1
  960. package/dist/taxonomies-BlrncHi9.mjs.map +0 -1
  961. package/dist/taxonomies-OHUIiIpI.mjs.map +0 -1
  962. package/dist/types-BcqdcT-r.d.mts.map +0 -1
  963. package/dist/types-C2pMvO62.d.mts.map +0 -1
  964. package/dist/types-CP1CflWf.mjs +0 -3
  965. package/dist/types-DN64_zzZ.d.mts.map +0 -1
  966. package/dist/types-DQIDRKF2.d.mts.map +0 -1
  967. package/dist/version-DB_fqCSI.mjs +0 -7
  968. package/dist/widgets-BD3ho7Rq.mjs.map +0 -1
  969. package/dist/zod-generator-LC-on_Vb.mjs.map +0 -1
  970. /package/dist/{api-tokens-rIXC6LdE.mjs → api-tokens-BfYts-sq.mjs} +0 -0
  971. /package/dist/{ssrf-DpXgKsna.mjs → ssrf-Dg3cIT-b.mjs} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-AZAsRsor.mjs","names":[],"sources":["../src/schema/registry.ts"],"sourcesContent":["import type {\n\tColumnDataType,\n\tCreateTableBuilder,\n\tInsertable,\n\tKysely,\n\tSelectable,\n\tUpdateable,\n} from \"kysely\";\nimport { sql } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { refreshDevTypes } from \"../astro/dev-typegen.js\";\nimport { currentTimestamp, listTablesLike, tableExists } from \"../database/dialect-helpers.js\";\nimport { withTransaction } from \"../database/transaction.js\";\nimport type { CollectionTable, Database, FieldTable } from \"../database/types.js\";\nimport { validateIdentifier } from \"../database/validate.js\";\nimport {\n\tcanResumeMediaUsageCollectionCapture,\n\tfinalizeMediaUsageCollectionCapture,\n\tinstallPreparedMediaUsageCollectionCapture,\n\tmarkMediaUsageCollectionCaptureReady,\n\tprepareMediaUsageCollectionCapture,\n} from \"../media/usage/activation.js\";\nimport {\n\tdeleteActivatedMediaUsageCollection,\n\tisMediaUsageCollectionSlugDeleting,\n} from \"../media/usage/collection-deletion.js\";\nimport {\n\tdeleteContentMediaUsageCollection,\n\tinvalidateContentMediaUsageSchemaChange,\n\tmarkContentMediaUsageCollectionStaleSafely,\n} from \"../media/usage/content-refresh.js\";\nimport { FTSManager } from \"../search/fts-manager.js\";\nimport { chunks, SQL_BATCH_SIZE } from \"../utils/chunks.js\";\nimport {\n\ttype Collection,\n\ttype CollectionAdminConfig,\n\ttype CollectionSource,\n\ttype CollectionSupport,\n\ttype ColumnType,\n\ttype Field,\n\ttype CreateCollectionInput,\n\ttype UpdateCollectionInput,\n\ttype CreateFieldInput,\n\ttype UpdateFieldInput,\n\ttype CollectionWithFields,\n\ttype FieldType,\n\tFIELD_TYPE_TO_COLUMN,\n\tisIndexableFieldType,\n\tRESERVED_FIELD_SLUGS,\n\tRESERVED_COLLECTION_SLUGS,\n} from \"./types.js\";\n\n// Regex patterns for schema registry\nconst SLUG_VALIDATION_PATTERN = /^[a-z][a-z0-9_]*$/;\nconst EC_PREFIX_PATTERN = /^ec_/;\nconst SINGLE_QUOTE_PATTERN = /'/g;\nconst UNDERSCORE_PATTERN = /_/g;\nconst WORD_BOUNDARY_PATTERN = /\\b\\w/g;\nconst FIELD_ID_PATTERN = /^[0-9A-Z]{26}$/;\n\n/** Valid column types for runtime validation */\nconst COLUMN_TYPES: ReadonlySet<string> = new Set([\"TEXT\", \"REAL\", \"INTEGER\", \"JSON\"]);\nconst COLUMN_TYPE_TO_DATA_TYPE = {\n\tTEXT: \"text\",\n\tREAL: \"real\",\n\tINTEGER: \"integer\",\n\tJSON: \"json\",\n} satisfies Record<ColumnType, ColumnDataType>;\nconst TEXT_ALIAS_FIELD_TYPES: ReadonlySet<FieldType> = new Set([\"string\", \"text\", \"slug\"]);\n\n/** Field types usable as a `titleField` — plain text that reads well as a title. */\nconst TITLE_FIELD_TYPES: ReadonlySet<string> = new Set([\"string\", \"text\", \"slug\"]);\n\n/** Valid collection source prefixes/values */\nconst VALID_SOURCES: ReadonlySet<string> = new Set([\"manual\", \"discovered\", \"seed\"]);\n\nfunction isCollectionSource(value: string): value is CollectionSource {\n\treturn VALID_SOURCES.has(value) || value.startsWith(\"template:\") || value.startsWith(\"import:\");\n}\n\nfunction isFieldType(value: string): value is FieldType {\n\treturn value in FIELD_TYPE_TO_COLUMN;\n}\n\nfunction isColumnType(value: string): value is ColumnType {\n\treturn COLUMN_TYPES.has(value);\n}\n\nconst VALID_COLLECTION_SUPPORTS: ReadonlySet<string> = new Set<CollectionSupport>([\n\t\"drafts\",\n\t\"revisions\",\n\t\"preview\",\n\t\"scheduling\",\n\t\"search\",\n\t\"seo\",\n]);\n\n// Each _emdash_fields row uses 16 bound parameters. Six rows keep every\n// multi-row INSERT below D1's 100-parameter statement limit.\nconst SEED_FIELD_INSERT_BATCH_SIZE = 6;\n\n/**\n * Rank given to collections without an explicit `sort_order`. SQLite sorts\n * NULL first on ASC while Postgres sorts it last, so the fallback is\n * materialised with COALESCE rather than left to the dialect.\n */\nconst UNORDERED_COLLECTION_RANK = 2147483647;\n\n/**\n * Collection ordering shared by every list read: explicit `sort_order`\n * first (ascending), then alphabetically by slug.\n */\nconst collectionOrder = sql<number>`coalesce(sort_order, ${sql.lit(UNORDERED_COLLECTION_RANK)})`;\n\nfunction assertIndexableField(type: FieldType, indexed: boolean | undefined, slug: string): void {\n\tif (indexed && !isIndexableFieldType(type)) {\n\t\tthrow new SchemaError(\n\t\t\t`Field \"${slug}\" cannot be indexed because type \"${type}\" is not a scalar query type`,\n\t\t\t\"FIELD_NOT_INDEXABLE\",\n\t\t);\n\t}\n}\n\nfunction isCollectionSupport(value: unknown): value is CollectionSupport {\n\treturn typeof value === \"string\" && VALID_COLLECTION_SUPPORTS.has(value);\n}\n\n/**\n * Parse a collection's `supports` column (stored as a JSON array of\n * CollectionSupport keys). Unknown/invalid entries are filtered out so the\n * runtime value matches the declared `CollectionSupport[]` type.\n *\n * Throws on malformed JSON so corruption surfaces loudly; returns an empty\n * array only for explicitly null/empty values or non-array JSON.\n */\nfunction parseSupports(raw: string | null | undefined): CollectionSupport[] {\n\tif (!raw) return [];\n\tconst parsed: unknown = JSON.parse(raw);\n\tif (!Array.isArray(parsed)) return [];\n\treturn parsed.filter(isCollectionSupport);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction parseCollectionAdmin(raw: string | null | undefined): CollectionAdminConfig | undefined {\n\tif (!raw) return undefined;\n\tconst parsed: unknown = JSON.parse(raw);\n\tif (!isRecord(parsed)) return undefined;\n\tconst listColumns = parsed.listColumns;\n\treturn {\n\t\tlistColumns: Array.isArray(listColumns)\n\t\t\t? listColumns.filter((value): value is string => typeof value === \"string\")\n\t\t\t: undefined,\n\t};\n}\n\nexport async function buildSeedCollectionCaptureFingerprint(\n\tinput: Omit<CreateCollectionInput, \"source\">,\n\tfields: readonly CreateFieldInput[],\n): Promise<string> {\n\tconst supports = input.supports ?? [\"drafts\", \"revisions\"];\n\tconst hasSeo = input.hasSeo ?? supports.includes(\"seo\") ?? false;\n\tlet maxSortOrder = -1;\n\tconst definitions = fields.map((field) => {\n\t\tconst sortOrder = field.sortOrder ?? maxSortOrder + 1;\n\t\tmaxSortOrder = Math.max(maxSortOrder, sortOrder);\n\t\treturn {\n\t\t\tslug: field.slug,\n\t\t\tlabel: field.label,\n\t\t\ttype: field.type,\n\t\t\trequired: field.required ?? false,\n\t\t\tunique: field.unique ?? false,\n\t\t\tdefaultValue: field.defaultValue === undefined ? null : JSON.stringify(field.defaultValue),\n\t\t\tvalidation: field.validation ? JSON.stringify(field.validation) : null,\n\t\t\twidget: field.widget ?? null,\n\t\t\toptions: field.options ? JSON.stringify(field.options) : null,\n\t\t\tsortOrder,\n\t\t\tsearchable: field.searchable ?? false,\n\t\t\ttranslatable: field.translatable ?? true,\n\t\t};\n\t});\n\tconst payload = JSON.stringify(\n\t\tcanonicalizeFingerprintValue({\n\t\t\tversion: 1,\n\t\t\tcollection: {\n\t\t\t\tslug: input.slug,\n\t\t\t\tlabel: input.label,\n\t\t\t\tlabelSingular: input.labelSingular ?? null,\n\t\t\t\tdescription: input.description ?? null,\n\t\t\t\ticon: input.icon ?? null,\n\t\t\t\tadmin: input.admin ?? null,\n\t\t\t\tsupports,\n\t\t\t\thasSeo,\n\t\t\t\thidden: input.hidden ?? false,\n\t\t\t\tsortOrder: input.sortOrder ?? null,\n\t\t\t\tcommentsEnabled: input.commentsEnabled ?? false,\n\t\t\t\turlPattern: input.urlPattern ?? null,\n\t\t\t\troutable: input.routable ?? true,\n\t\t\t},\n\t\t\tfields: definitions,\n\t\t}),\n\t);\n\tconst digest = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(payload));\n\tconst hex = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, \"0\")).join(\n\t\t\"\",\n\t);\n\treturn `media-usage-seed:v1:sha256:${hex}`;\n}\n\nfunction canonicalizeFingerprintValue(value: unknown): unknown {\n\tif (value === undefined) return { __emdashUndefined: true };\n\tif (Array.isArray(value)) return value.map(canonicalizeFingerprintValue);\n\tif (typeof value !== \"object\" || value === null) return value;\n\n\tconst canonical: Record<string, unknown> = {};\n\tfor (const [key, entry] of Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b))) {\n\t\tcanonical[key] = canonicalizeFingerprintValue(entry);\n\t}\n\treturn canonical;\n}\n\n/**\n * Error thrown when a schema operation fails\n */\nexport class SchemaError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic code: string,\n\t\tpublic details?: Record<string, unknown>,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"SchemaError\";\n\t}\n}\n\n/**\n * Schema Registry\n *\n * Manages collection and field definitions stored in D1.\n * Handles runtime DDL operations (CREATE TABLE, ALTER TABLE).\n */\nexport class SchemaRegistry {\n\tconstructor(private db: Kysely<Database>) {}\n\n\t/**\n\t * Notify the dev typegen hook that the schema has changed.\n\t */\n\tprivate notifyTypegen(): void {\n\t\trefreshDevTypes(this.db);\n\t}\n\n\t// ============================================\n\t// Collection Operations\n\t// ============================================\n\n\t/**\n\t * List all collections\n\t */\n\tasync listCollections(): Promise<Collection[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(collectionOrder, \"asc\")\n\t\t\t.orderBy(\"slug\", \"asc\")\n\t\t\t.execute();\n\n\t\treturn rows.map(this.mapCollectionRow);\n\t}\n\n\t/**\n\t * Get a collection by slug\n\t */\n\tasync getCollection(slug: string): Promise<Collection | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.where(\"slug\", \"=\", slug)\n\t\t\t.selectAll()\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? this.mapCollectionRow(row) : null;\n\t}\n\n\t/**\n\t * Get a collection with all its fields\n\t */\n\tasync getCollectionWithFields(slug: string): Promise<CollectionWithFields | null> {\n\t\tconst collection = await this.getCollection(slug);\n\t\tif (!collection) return null;\n\n\t\tconst fields = await this.listFields(collection.id);\n\n\t\treturn { ...collection, fields };\n\t}\n\n\t/**\n\t * List every collection together with its fields in O(1) query shapes\n\t * — one for collections, then one batched query for the fields of every\n\t * returned collection — instead of the N+1 pattern of `listCollections`\n\t * + per-collection `listFields`. The fields query is chunked at\n\t * `SQL_BATCH_SIZE` to stay under D1's bound-parameter limit, so on\n\t * sites with more than `SQL_BATCH_SIZE` collections the field fetch\n\t * becomes `ceil(collectionCount / SQL_BATCH_SIZE)` queries — still\n\t * a constant factor, not N+1. Typical sites have well under\n\t * `SQL_BATCH_SIZE` collections, so this is two queries in practice.\n\t *\n\t * Used by the manifest build, which previously paid N+1 round-trips on\n\t * every admin request. Each round-trip costs ~80–150ms against the D1\n\t * primary on a busy link, so a 10-collection site spent ~1 s rebuilding\n\t * a manifest that is now built fresh per admin request (no cache).\n\t */\n\tasync listCollectionsWithFields(): Promise<CollectionWithFields[]> {\n\t\tconst collectionRows = await this.db\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(collectionOrder, \"asc\")\n\t\t\t.orderBy(\"slug\", \"asc\")\n\t\t\t.execute();\n\n\t\tif (collectionRows.length === 0) return [];\n\n\t\tconst fieldsByCollection = new Map<string, Field[]>();\n\t\t// Chunk to stay under D1's bound-parameter limit. Typical sites have\n\t\t// well under SQL_BATCH_SIZE collections, so this is a single query\n\t\t// in practice; on larger sites it becomes a small constant number\n\t\t// of queries, never N+1.\n\t\tfor (const idChunk of chunks(\n\t\t\tcollectionRows.map((c) => c.id),\n\t\t\tSQL_BATCH_SIZE,\n\t\t)) {\n\t\t\tconst fieldRows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t\t.where(\"collection_id\", \"in\", idChunk)\n\t\t\t\t.selectAll()\n\t\t\t\t.orderBy(\"collection_id\", \"asc\")\n\t\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t\t.orderBy(\"created_at\", \"asc\")\n\t\t\t\t.execute();\n\t\t\tfor (const row of fieldRows) {\n\t\t\t\tconst list = fieldsByCollection.get(row.collection_id) ?? [];\n\t\t\t\tlist.push(this.mapFieldRow(row));\n\t\t\t\tfieldsByCollection.set(row.collection_id, list);\n\t\t\t}\n\t\t}\n\n\t\treturn collectionRows.map((c) => ({\n\t\t\t...this.mapCollectionRow(c),\n\t\t\tfields: fieldsByCollection.get(c.id) ?? [],\n\t\t}));\n\t}\n\n\t/**\n\t * Validate `titleField`/`dateField` against the collection's fields:\n\t * `titleField` must be a text-like field; `dateField` must be a `datetime` field.\n\t * Only truthy values are checked (undefined = unchanged, null/\"\" = cleared).\n\t */\n\tprivate async validateTitleDateFields(\n\t\tcollectionId: string,\n\t\tcollectionSlug: string,\n\t\tinput: { titleField?: string | null; dateField?: string | null },\n\t\tdb: Kysely<Database> = this.db,\n\t): Promise<void> {\n\t\tconst slugs = [input.titleField, input.dateField].filter((slug): slug is string => !!slug);\n\t\tif (slugs.length === 0) return;\n\n\t\tconst rows = await db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.where(\"collection_id\", \"=\", collectionId)\n\t\t\t.where(\"slug\", \"in\", slugs)\n\t\t\t.select([\"slug\", \"type\"])\n\t\t\t.execute();\n\t\tconst typeBySlug = new Map(rows.map((row) => [row.slug, row.type]));\n\n\t\tif (input.titleField) {\n\t\t\tconst type = typeBySlug.get(input.titleField);\n\t\t\tif (type === undefined) {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`titleField \"${input.titleField}\" is not a field on \"${collectionSlug}\"`,\n\t\t\t\t\t\"INVALID_TITLE_FIELD\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!TITLE_FIELD_TYPES.has(type)) {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`titleField \"${input.titleField}\" must be a text field (got \"${type}\")`,\n\t\t\t\t\t\"INVALID_TITLE_FIELD\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (input.dateField) {\n\t\t\tconst type = typeBySlug.get(input.dateField);\n\t\t\tif (type === undefined) {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`dateField \"${input.dateField}\" is not a field on \"${collectionSlug}\"`,\n\t\t\t\t\t\"INVALID_DATE_FIELD\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (type !== \"datetime\") {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`dateField \"${input.dateField}\" must be a datetime field (got \"${type}\")`,\n\t\t\t\t\t\"INVALID_DATE_FIELD\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Create a new collection\n\t */\n\tasync createCollection(input: CreateCollectionInput): Promise<Collection> {\n\t\t// Validate slug\n\t\tthis.validateSlug(input.slug, \"collection\");\n\t\tif (RESERVED_COLLECTION_SLUGS.includes(input.slug)) {\n\t\t\tthrow new SchemaError(`Collection slug \"${input.slug}\" is reserved`, \"RESERVED_SLUG\");\n\t\t}\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\t// Check if collection already exists\n\t\tconst existing = await this.getCollection(input.slug);\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\t\tif (\n\t\t\texisting &&\n\t\t\t!(await canResumeMediaUsageCollectionCapture(this.db, {\n\t\t\t\tcollectionId: existing.id,\n\t\t\t\tcollectionSlug: existing.slug,\n\t\t\t}))\n\t\t) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\tconst proposedId = existing?.id ?? ulid();\n\n\t\t// Default `supports` to drafts + revisions when the caller didn't\n\t\t// specify it. Explicit empty array (`[]`) is preserved as an opt-out\n\t\t// — only `undefined` triggers the default. This is the canonical\n\t\t// default for new collections; the MCP and admin UI layers used to\n\t\t// duplicate this default but now defer to the registry.\n\t\tconst supports = input.supports ?? [\"drafts\", \"revisions\"];\n\n\t\t// Insert collection record and create content table in a transaction\n\t\t// so a failure in table creation doesn't leave an orphaned row.\n\t\t// Uses withTransaction for D1 compatibility (no transaction support).\n\t\t// Derive hasSeo from supports array if not explicitly set\n\t\tconst hasSeo = input.hasSeo ?? supports.includes(\"seo\") ?? false;\n\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tconst capture = await prepareMediaUsageCollectionCapture(trx, {\n\t\t\t\tcollectionId: proposedId,\n\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\tregisteredCollectionId: existing?.id,\n\t\t\t});\n\t\t\tconst values: Insertable<CollectionTable> = {\n\t\t\t\tid: capture.collectionId,\n\t\t\t\tslug: input.slug,\n\t\t\t\tlabel: input.label,\n\t\t\t\tlabel_singular: input.labelSingular ?? null,\n\t\t\t\tdescription: input.description ?? null,\n\t\t\t\ticon: input.icon ?? null,\n\t\t\t\tadmin_config: input.admin ? JSON.stringify(input.admin) : null,\n\t\t\t\tsupports: JSON.stringify(supports),\n\t\t\t\tsource: input.source ?? \"manual\",\n\t\t\t\thas_seo: hasSeo ? 1 : 0,\n\t\t\t\troutable: input.routable === false ? 0 : 1,\n\t\t\t\thidden: input.hidden ? 1 : 0,\n\t\t\t\tsort_order: input.sortOrder ?? null,\n\t\t\t\tcomments_enabled: input.commentsEnabled ? 1 : 0,\n\t\t\t\turl_pattern: input.urlPattern ?? null,\n\t\t\t};\n\n\t\t\tif (capture.captureRequired) {\n\t\t\t\tawait this.createContentTable(input.slug, trx, [], {\n\t\t\t\t\tifNotExists: capture.resuming,\n\t\t\t\t});\n\t\t\t\tawait installPreparedMediaUsageCollectionCapture(trx, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t});\n\t\t\t\tawait markMediaUsageCollectionCaptureReady(trx, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t});\n\t\t\t\tif (!capture.registrationExists) {\n\t\t\t\t\tawait trx.insertInto(\"_emdash_collections\").values(values).execute();\n\t\t\t\t}\n\t\t\t\tawait finalizeMediaUsageCollectionCapture(trx, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait trx.insertInto(\"_emdash_collections\").values(values).execute();\n\t\t\tawait this.createContentTable(input.slug, trx);\n\t\t});\n\n\t\tconst collection = await this.getCollection(input.slug);\n\t\tif (!collection) {\n\t\t\tthrow new SchemaError(\"Failed to create collection\", \"CREATE_FAILED\");\n\t\t}\n\n\t\tthis.notifyTypegen();\n\t\treturn collection;\n\t}\n\n\t/**\n\t * Create a seed-owned collection and all of its fields in bulk.\n\t *\n\t * Fresh seeds can define dozens of fields. Creating them through\n\t * `createField` performs multiple reads, one ALTER TABLE, and one media\n\t * usage invalidation per field, which can exhaust D1's per-request query\n\t * budget. This path validates the full schema before mutating it, creates\n\t * the complete content table in one statement, and inserts field metadata\n\t * in parameter-safe batches.\n\t */\n\tasync createSeedCollection(\n\t\tinput: Omit<CreateCollectionInput, \"source\">,\n\t\tfields: readonly CreateFieldInput[],\n\t): Promise<void> {\n\t\tthis.validateSlug(input.slug, \"collection\");\n\t\tif (RESERVED_COLLECTION_SLUGS.includes(input.slug)) {\n\t\t\tthrow new SchemaError(`Collection slug \"${input.slug}\" is reserved`, \"RESERVED_SLUG\");\n\t\t}\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\tconst fieldSlugs = new Set<string>();\n\t\tfor (const field of fields) {\n\t\t\tthis.validateSlug(field.slug, \"field\");\n\t\t\tassertIndexableField(field.type, field.indexed, field.slug);\n\t\t\tif (RESERVED_FIELD_SLUGS.includes(field.slug)) {\n\t\t\t\tthrow new SchemaError(`Field slug \"${field.slug}\" is reserved`, \"RESERVED_SLUG\");\n\t\t\t}\n\t\t\tif (fieldSlugs.has(field.slug)) {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`Field \"${field.slug}\" already exists in collection \"${input.slug}\"`,\n\t\t\t\t\t\"FIELD_EXISTS\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tfieldSlugs.add(field.slug);\n\t\t}\n\n\t\tconst supports = input.supports ?? [\"drafts\", \"revisions\"];\n\t\tconst hasSeo = input.hasSeo ?? supports.includes(\"seo\") ?? false;\n\t\tconst creationFingerprint = await buildSeedCollectionCaptureFingerprint(input, fields);\n\t\tconst existing = await this.getCollection(input.slug);\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\t\tif (\n\t\t\texisting &&\n\t\t\t!(await canResumeMediaUsageCollectionCapture(this.db, {\n\t\t\t\tcollectionId: existing.id,\n\t\t\t\tcollectionSlug: existing.slug,\n\t\t\t\tcreationFingerprint,\n\t\t\t}))\n\t\t) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\tconst proposedCollectionId = existing?.id ?? ulid();\n\t\tlet maxSortOrder = -1;\n\t\tconst fieldRows: Insertable<FieldTable>[] = fields.map((field) => {\n\t\t\tconst sortOrder = field.sortOrder ?? maxSortOrder + 1;\n\t\t\tmaxSortOrder = Math.max(maxSortOrder, sortOrder);\n\n\t\t\treturn {\n\t\t\t\tid: ulid(),\n\t\t\t\tcollection_id: proposedCollectionId,\n\t\t\t\tslug: field.slug,\n\t\t\t\tlabel: field.label,\n\t\t\t\ttype: field.type,\n\t\t\t\tcolumn_type: FIELD_TYPE_TO_COLUMN[field.type],\n\t\t\t\trequired: field.required ? 1 : 0,\n\t\t\t\tunique: field.unique ? 1 : 0,\n\t\t\t\tdefault_value: field.defaultValue !== undefined ? JSON.stringify(field.defaultValue) : null,\n\t\t\t\tvalidation: field.validation ? JSON.stringify(field.validation) : null,\n\t\t\t\twidget: field.widget ?? null,\n\t\t\t\toptions: field.options ? JSON.stringify(field.options) : null,\n\t\t\t\tsort_order: sortOrder,\n\t\t\t\tsearchable: field.searchable ? 1 : 0,\n\t\t\t\tindexed: field.indexed ? 1 : 0,\n\t\t\t\ttranslatable: field.translatable === false ? 0 : 1,\n\t\t\t};\n\t\t});\n\n\t\tlet schemaMutated = false;\n\t\ttry {\n\t\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\t\tconst capture = await prepareMediaUsageCollectionCapture(trx, {\n\t\t\t\t\tcollectionId: proposedCollectionId,\n\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t\tcreationFingerprint,\n\t\t\t\t\tregisteredCollectionId: existing?.id,\n\t\t\t\t});\n\t\t\t\tconst collectionValues: Insertable<CollectionTable> = {\n\t\t\t\t\tid: capture.collectionId,\n\t\t\t\t\tslug: input.slug,\n\t\t\t\t\tlabel: input.label,\n\t\t\t\t\tlabel_singular: input.labelSingular ?? null,\n\t\t\t\t\tdescription: input.description ?? null,\n\t\t\t\t\ticon: input.icon ?? null,\n\t\t\t\t\tadmin_config: input.admin ? JSON.stringify(input.admin) : null,\n\t\t\t\t\tsupports: JSON.stringify(supports),\n\t\t\t\t\tsource: \"seed\",\n\t\t\t\t\thas_seo: hasSeo ? 1 : 0,\n\t\t\t\t\troutable: input.routable === false ? 0 : 1,\n\t\t\t\t\thidden: input.hidden ? 1 : 0,\n\t\t\t\t\tsort_order: input.sortOrder ?? null,\n\t\t\t\t\tcomments_enabled: input.commentsEnabled ? 1 : 0,\n\t\t\t\t\turl_pattern: input.urlPattern ?? null,\n\t\t\t\t};\n\t\t\t\tconst rows = fieldRows.map((row) => ({\n\t\t\t\t\t...row,\n\t\t\t\t\tcollection_id: capture.collectionId,\n\t\t\t\t}));\n\n\t\t\t\tif (capture.captureRequired) {\n\t\t\t\t\tawait this.createContentTable(input.slug, trx, fields, {\n\t\t\t\t\t\tifNotExists: capture.resuming,\n\t\t\t\t\t});\n\t\t\t\t\tawait installPreparedMediaUsageCollectionCapture(trx, {\n\t\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t\t});\n\t\t\t\t\tawait markMediaUsageCollectionCaptureReady(trx, {\n\t\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t\t});\n\t\t\t\t\tif (!capture.registrationExists) {\n\t\t\t\t\t\tawait trx.insertInto(\"_emdash_collections\").values(collectionValues).execute();\n\t\t\t\t\t\tschemaMutated = true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tawait trx.insertInto(\"_emdash_collections\").values(collectionValues).execute();\n\t\t\t\t\tschemaMutated = true;\n\t\t\t\t\tawait this.createContentTable(input.slug, trx, fields);\n\t\t\t\t}\n\n\t\t\t\tfor (const fieldBatch of chunks(rows, SEED_FIELD_INSERT_BATCH_SIZE)) {\n\t\t\t\t\tlet insert = trx.insertInto(\"_emdash_fields\").values(fieldBatch);\n\t\t\t\t\tif (capture.resuming) {\n\t\t\t\t\t\tinsert = insert.onConflict((conflict) =>\n\t\t\t\t\t\t\tconflict.columns([\"collection_id\", \"slug\"]).doNothing(),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tawait insert.execute();\n\t\t\t\t}\n\t\t\t\tlet indexedRows: readonly { id: string; slug: string; indexed?: number }[] = rows;\n\t\t\t\tif (capture.resuming) {\n\t\t\t\t\tindexedRows = await this.assertSeedFieldDefinitions(capture.collectionId, fields, trx);\n\t\t\t\t}\n\t\t\t\tfor (const field of indexedRows) {\n\t\t\t\t\tif (field.indexed === 1) {\n\t\t\t\t\t\tawait this.createFieldIndex(input.slug, field.id, field.slug, trx);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (capture.captureRequired) {\n\t\t\t\t\tawait finalizeMediaUsageCollectionCapture(trx, {\n\t\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait markContentMediaUsageCollectionStaleSafely(this.db, input.slug, \"CONTENT_USAGE_STALE\");\n\t\t} catch (error) {\n\t\t\tif (schemaMutated) {\n\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\tthis.db,\n\t\t\t\t\tinput.slug,\n\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tthis.notifyTypegen();\n\t}\n\n\tprivate async assertSeedFieldDefinitions(\n\t\tcollectionId: string,\n\t\tfields: readonly CreateFieldInput[],\n\t\tdb: Kysely<Database>,\n\t): Promise<Selectable<FieldTable>[]> {\n\t\tconst stored = await db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.selectAll()\n\t\t\t.where(\"collection_id\", \"=\", collectionId)\n\t\t\t.execute();\n\t\tif (stored.length !== fields.length) {\n\t\t\tthrow new SchemaError(\"Interrupted seed collection fields do not match\", \"CREATE_FAILED\");\n\t\t}\n\n\t\tlet maxSortOrder = -1;\n\t\tfor (const field of fields) {\n\t\t\tconst sortOrder = field.sortOrder ?? maxSortOrder + 1;\n\t\t\tmaxSortOrder = Math.max(maxSortOrder, sortOrder);\n\t\t\tconst row = stored.find((candidate) => candidate.slug === field.slug);\n\t\t\tif (\n\t\t\t\t!row ||\n\t\t\t\trow.label !== field.label ||\n\t\t\t\trow.type !== field.type ||\n\t\t\t\trow.column_type !== FIELD_TYPE_TO_COLUMN[field.type] ||\n\t\t\t\trow.required !== (field.required ? 1 : 0) ||\n\t\t\t\trow.unique !== (field.unique ? 1 : 0) ||\n\t\t\t\trow.default_value !==\n\t\t\t\t\t(field.defaultValue !== undefined ? JSON.stringify(field.defaultValue) : null) ||\n\t\t\t\trow.validation !== (field.validation ? JSON.stringify(field.validation) : null) ||\n\t\t\t\trow.widget !== (field.widget ?? null) ||\n\t\t\t\trow.options !== (field.options ? JSON.stringify(field.options) : null) ||\n\t\t\t\trow.sort_order !== sortOrder ||\n\t\t\t\trow.searchable !== (field.searchable ? 1 : 0) ||\n\t\t\t\trow.indexed !== (field.indexed ? 1 : 0) ||\n\t\t\t\trow.translatable !== (field.translatable === false ? 0 : 1)\n\t\t\t) {\n\t\t\t\tthrow new SchemaError(\"Interrupted seed collection fields do not match\", \"CREATE_FAILED\");\n\t\t\t}\n\t\t}\n\t\treturn stored;\n\t}\n\n\t/**\n\t * Update a collection\n\t */\n\tasync updateCollection(slug: string, input: UpdateCollectionInput): Promise<Collection> {\n\t\tconst updated = await withTransaction(this.db, async (trx) => {\n\t\t\tconst existingRow = await trx\n\t\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t\t.where(\"slug\", \"=\", slug)\n\t\t\t\t.selectAll()\n\t\t\t\t.executeTakeFirst();\n\t\t\tif (!existingRow) {\n\t\t\t\tthrow new SchemaError(`Collection \"${slug}\" not found`, \"COLLECTION_NOT_FOUND\");\n\t\t\t}\n\t\t\tconst existing = this.mapCollectionRow(existingRow);\n\t\t\tawait this.validateTitleDateFields(\n\t\t\t\texisting.id,\n\t\t\t\tslug,\n\t\t\t\t{\n\t\t\t\t\ttitleField: input.titleField,\n\t\t\t\t\tdateField: input.dateField,\n\t\t\t\t},\n\t\t\t\ttrx,\n\t\t\t);\n\t\t\tconst updates: Updateable<CollectionTable> = {};\n\n\t\t\tif (input.label !== undefined) updates.label = input.label;\n\t\t\tif (input.labelSingular !== undefined) updates.label_singular = input.labelSingular;\n\t\t\tif (input.description !== undefined) updates.description = input.description;\n\t\t\tif (input.icon !== undefined) updates.icon = input.icon;\n\t\t\tif (input.admin !== undefined) updates.admin_config = JSON.stringify(input.admin);\n\t\t\tif (input.supports !== undefined) updates.supports = JSON.stringify(input.supports);\n\t\t\tif (input.urlPattern !== undefined) updates.url_pattern = input.urlPattern;\n\t\t\tif (input.routable !== undefined) updates.routable = input.routable ? 1 : 0;\n\t\t\tif (input.hasSeo !== undefined) {\n\t\t\t\tupdates.has_seo = input.hasSeo ? 1 : 0;\n\t\t\t} else if (input.supports !== undefined) {\n\t\t\t\tupdates.has_seo = input.supports.includes(\"seo\") ? 1 : 0;\n\t\t\t}\n\t\t\tif (input.hidden !== undefined) updates.hidden = input.hidden ? 1 : 0;\n\t\t\tif (input.sortOrder !== undefined) updates.sort_order = input.sortOrder;\n\t\t\tif (input.titleField !== undefined) updates.title_field = input.titleField || null;\n\t\t\tif (input.dateField !== undefined) updates.date_field = input.dateField || null;\n\t\t\tif (input.commentsEnabled !== undefined) {\n\t\t\t\tupdates.comments_enabled = input.commentsEnabled ? 1 : 0;\n\t\t\t}\n\t\t\tif (input.commentsModeration !== undefined) {\n\t\t\t\tupdates.comments_moderation = input.commentsModeration;\n\t\t\t}\n\t\t\tif (input.commentsClosedAfterDays !== undefined) {\n\t\t\t\tupdates.comments_closed_after_days = input.commentsClosedAfterDays;\n\t\t\t}\n\t\t\tif (input.commentsAutoApproveUsers !== undefined) {\n\t\t\t\tupdates.comments_auto_approve_users = input.commentsAutoApproveUsers ? 1 : 0;\n\t\t\t}\n\n\t\t\tupdates.updated_at = new Date().toISOString();\n\t\t\tawait trx\n\t\t\t\t.updateTable(\"_emdash_collections\")\n\t\t\t\t.set(updates)\n\t\t\t\t.where(\"id\", \"=\", existing.id)\n\t\t\t\t.execute();\n\n\t\t\tconst row = await trx\n\t\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t\t.where(\"slug\", \"=\", slug)\n\t\t\t\t.selectAll()\n\t\t\t\t.executeTakeFirst();\n\n\t\t\tif (!row) {\n\t\t\t\tthrow new SchemaError(\"Failed to update collection\", \"UPDATE_FAILED\");\n\t\t\t}\n\n\t\t\t// Sync FTS state when the supports array changes (e.g. search toggled on/off)\n\t\t\tif (input.supports !== undefined) {\n\t\t\t\tconst hadSearch = existing.supports.includes(\"search\");\n\t\t\t\tconst hasSearch = parseSupports(row.supports).includes(\"search\");\n\t\t\t\tif (hadSearch !== hasSearch) {\n\t\t\t\t\tawait this.syncSearchState(slug, trx);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.mapCollectionRow(row);\n\t\t});\n\t\tthis.notifyTypegen();\n\t\treturn updated;\n\t}\n\n\t/**\n\t * Delete a collection\n\t */\n\tasync deleteCollection(slug: string, options?: { force?: boolean }): Promise<void> {\n\t\tconst existing = await this.getCollection(slug);\n\t\tif (existing && !options?.force && (await this.collectionHasContent(slug))) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Collection \"${slug}\" has content. Use force: true to delete.`,\n\t\t\t\t\"COLLECTION_HAS_CONTENT\",\n\t\t\t);\n\t\t}\n\t\tconst activated = await deleteActivatedMediaUsageCollection(this.db, {\n\t\t\tcollectionId: existing?.id,\n\t\t\tcollectionSlug: slug,\n\t\t\tforceDelete: options?.force === true,\n\t\t});\n\t\tif (activated === \"has_content\") {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Collection \"${slug}\" has content. Use force: true to delete.`,\n\t\t\t\t\"COLLECTION_HAS_CONTENT\",\n\t\t\t);\n\t\t}\n\t\tif (activated === \"in_progress\") {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" deletion is already in progress`, \"CONFLICT\");\n\t\t}\n\t\tif (activated === \"deleted\") return;\n\t\tif (!existing) {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" not found`, \"COLLECTION_NOT_FOUND\");\n\t\t}\n\n\t\tlet contentTableDropped = false;\n\t\ttry {\n\t\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\t\t// Drop FTS table and triggers before dropping the content table\n\t\t\t\tconst ftsManager = new FTSManager(trx);\n\t\t\t\tawait ftsManager.dropFtsTable(slug);\n\n\t\t\t\t// Drop the content table\n\t\t\t\tconst tableName = this.getTableName(slug);\n\t\t\t\tawait sql`DROP TABLE IF EXISTS ${sql.ref(tableName)}`.execute(trx);\n\t\t\t\tcontentTableDropped = true;\n\n\t\t\t\t// Delete the collection record (fields will cascade)\n\t\t\t\tawait trx.deleteFrom(\"_emdash_collections\").where(\"id\", \"=\", existing.id).execute();\n\t\t\t});\n\t\t\tawait deleteContentMediaUsageCollection(this.db, slug);\n\t\t} catch (error) {\n\t\t\tif (contentTableDropped && !(await tableExists(this.db, this.getTableName(slug)))) {\n\t\t\t\tawait deleteContentMediaUsageCollection(this.db, slug);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tthis.notifyTypegen();\n\t}\n\n\t// ============================================\n\t// Field Operations\n\t// ============================================\n\n\t/**\n\t * List fields for a collection\n\t */\n\tasync listFields(collectionId: string): Promise<Field[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.where(\"collection_id\", \"=\", collectionId)\n\t\t\t.selectAll()\n\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t.orderBy(\"created_at\", \"asc\")\n\t\t\t.execute();\n\n\t\treturn rows.map(this.mapFieldRow);\n\t}\n\n\t/**\n\t * Get a field by slug within a collection\n\t */\n\tasync getField(collectionSlug: string, fieldSlug: string): Promise<Field | null> {\n\t\tconst collection = await this.getCollection(collectionSlug);\n\t\tif (!collection) return null;\n\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t\t.where(\"slug\", \"=\", fieldSlug)\n\t\t\t.selectAll()\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? this.mapFieldRow(row) : null;\n\t}\n\n\t/**\n\t * Create a new field\n\t */\n\tasync createField(collectionSlug: string, input: CreateFieldInput): Promise<Field> {\n\t\tconst collection = await this.getCollection(collectionSlug);\n\t\tif (!collection) {\n\t\t\tthrow new SchemaError(`Collection \"${collectionSlug}\" not found`, \"COLLECTION_NOT_FOUND\");\n\t\t}\n\n\t\t// Validate slug\n\t\tthis.validateSlug(input.slug, \"field\");\n\t\tif (RESERVED_FIELD_SLUGS.includes(input.slug)) {\n\t\t\tthrow new SchemaError(`Field slug \"${input.slug}\" is reserved`, \"RESERVED_SLUG\");\n\t\t}\n\n\t\t// Check if field already exists\n\t\tconst existing = await this.getField(collectionSlug, input.slug);\n\t\tif (existing) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Field \"${input.slug}\" already exists in collection \"${collectionSlug}\"`,\n\t\t\t\t\"FIELD_EXISTS\",\n\t\t\t);\n\t\t}\n\n\t\tconst id = ulid();\n\t\tconst columnType = FIELD_TYPE_TO_COLUMN[input.type];\n\t\tassertIndexableField(input.type, input.indexed, input.slug);\n\n\t\t// Get max sort order\n\t\tconst maxSort = await this.db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t\t.select((eb) => eb.fn.max<number>(\"sort_order\").as(\"max\"))\n\t\t\t.executeTakeFirst();\n\n\t\tconst sortOrder = input.sortOrder ?? (maxSort?.max ?? -1) + 1;\n\t\tconst activeCoverageInvalidated = await invalidateContentMediaUsageSchemaChange(\n\t\t\tthis.db,\n\t\t\tcollectionSlug,\n\t\t);\n\n\t\tlet schemaMutated = false;\n\t\ttry {\n\t\t\tconst created = await withTransaction(this.db, async (trx) => {\n\t\t\t\t// Insert field record\n\t\t\t\tawait trx\n\t\t\t\t\t.insertInto(\"_emdash_fields\")\n\t\t\t\t\t.values({\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tcollection_id: collection.id,\n\t\t\t\t\t\tslug: input.slug,\n\t\t\t\t\t\tlabel: input.label,\n\t\t\t\t\t\ttype: input.type,\n\t\t\t\t\t\tcolumn_type: columnType,\n\t\t\t\t\t\trequired: input.required ? 1 : 0,\n\t\t\t\t\t\tunique: input.unique ? 1 : 0,\n\t\t\t\t\t\tdefault_value:\n\t\t\t\t\t\t\tinput.defaultValue !== undefined ? JSON.stringify(input.defaultValue) : null,\n\t\t\t\t\t\tvalidation: input.validation ? JSON.stringify(input.validation) : null,\n\t\t\t\t\t\twidget: input.widget ?? null,\n\t\t\t\t\t\toptions: input.options ? JSON.stringify(input.options) : null,\n\t\t\t\t\t\tsort_order: sortOrder,\n\t\t\t\t\t\tsearchable: input.searchable ? 1 : 0,\n\t\t\t\t\t\tindexed: input.indexed ? 1 : 0,\n\t\t\t\t\t\ttranslatable: input.translatable === false ? 0 : 1,\n\t\t\t\t\t})\n\t\t\t\t\t.execute();\n\t\t\t\tschemaMutated = true;\n\n\t\t\t\t// Add column to content table — pass trx to stay on the same connection\n\t\t\t\tawait this.addColumn(\n\t\t\t\t\tcollectionSlug,\n\t\t\t\t\tinput.slug,\n\t\t\t\t\tinput.type,\n\t\t\t\t\t{\n\t\t\t\t\t\trequired: input.required,\n\t\t\t\t\t\tdefaultValue: input.defaultValue,\n\t\t\t\t\t},\n\t\t\t\t\ttrx,\n\t\t\t\t);\n\n\t\t\t\tif (input.indexed) {\n\t\t\t\t\tawait this.createFieldIndex(collectionSlug, id, input.slug, trx);\n\t\t\t\t}\n\n\t\t\t\t// Read the created field via trx (not this.db) to avoid connection mutex deadlock\n\t\t\t\tconst fieldRow = await trx\n\t\t\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t\t\t\t.where(\"slug\", \"=\", input.slug)\n\t\t\t\t\t.selectAll()\n\t\t\t\t\t.executeTakeFirst();\n\n\t\t\t\tif (!fieldRow) {\n\t\t\t\t\tthrow new SchemaError(\"Failed to create field\", \"CREATE_FAILED\");\n\t\t\t\t}\n\n\t\t\t\tconst field = this.mapFieldRow(fieldRow);\n\n\t\t\t\t// Sync search state if this field is searchable; support checks are handled by syncSearchState()\n\t\t\t\tif (input.searchable) {\n\t\t\t\t\tawait this.syncSearchState(collectionSlug, trx);\n\t\t\t\t}\n\n\t\t\t\treturn field;\n\t\t\t});\n\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t} else {\n\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\tthis.db,\n\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tthis.notifyTypegen();\n\t\t\treturn created;\n\t\t} catch (error) {\n\t\t\tif (schemaMutated) {\n\t\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t\t} else {\n\t\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\t\tthis.db,\n\t\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Update a field\n\t */\n\tasync updateField(\n\t\tcollectionSlug: string,\n\t\tfieldSlug: string,\n\t\tinput: UpdateFieldInput,\n\t): Promise<Field> {\n\t\tlet activeCoverageInvalidated = false;\n\t\tlet schemaMutated = false;\n\t\ttry {\n\t\t\tconst updatedField = await withTransaction(this.db, async (trx) => {\n\t\t\t\tconst collectionRow = await trx\n\t\t\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t\t\t.where(\"slug\", \"=\", collectionSlug)\n\t\t\t\t\t.select([\"id\", \"title_field\", \"date_field\"])\n\t\t\t\t\t.executeTakeFirst();\n\t\t\t\tconst fieldRow = collectionRow\n\t\t\t\t\t? await trx\n\t\t\t\t\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t\t\t\t\t.where(\"collection_id\", \"=\", collectionRow.id)\n\t\t\t\t\t\t\t.where(\"slug\", \"=\", fieldSlug)\n\t\t\t\t\t\t\t.selectAll()\n\t\t\t\t\t\t\t.executeTakeFirst()\n\t\t\t\t\t: undefined;\n\t\t\t\tif (!fieldRow) {\n\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t`Field \"${fieldSlug}\" not found in collection \"${collectionSlug}\"`,\n\t\t\t\t\t\t\"FIELD_NOT_FOUND\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst field = this.mapFieldRow(fieldRow);\n\t\t\t\tconst updates: Updateable<FieldTable> = {};\n\t\t\t\tlet nextType = field.type;\n\n\t\t\t\tif (input.type !== undefined && input.type !== field.type) {\n\t\t\t\t\tconst newColumnType = FIELD_TYPE_TO_COLUMN[input.type];\n\t\t\t\t\tif (newColumnType !== field.columnType) {\n\t\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t\t`Cannot change field \"${fieldSlug}\" in collection \"${collectionSlug}\" from type ` +\n\t\t\t\t\t\t\t\t`\"${field.type}\" to \"${input.type}\": the underlying column type would change from ` +\n\t\t\t\t\t\t\t\t`${field.columnType} to ${newColumnType}, which requires a manual content migration.`,\n\t\t\t\t\t\t\t\"FIELD_TYPE_COLUMN_CHANGE\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (!TEXT_ALIAS_FIELD_TYPES.has(field.type) || !TEXT_ALIAS_FIELD_TYPES.has(input.type)) {\n\t\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t\t`Cannot change field \"${fieldSlug}\" in collection \"${collectionSlug}\" from type ` +\n\t\t\t\t\t\t\t\t`\"${field.type}\" to \"${input.type}\" without a manual content migration.`,\n\t\t\t\t\t\t\t\"FIELD_TYPE_CHANGE_REQUIRES_MIGRATION\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (collectionRow?.title_field === fieldSlug && !TITLE_FIELD_TYPES.has(input.type)) {\n\t\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t\t`titleField \"${fieldSlug}\" must stay a text field, not \"${input.type}\"`,\n\t\t\t\t\t\t\t\"INVALID_TITLE_FIELD\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (collectionRow?.date_field === fieldSlug && input.type !== \"datetime\") {\n\t\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t\t`dateField \"${fieldSlug}\" must stay a datetime field, not \"${input.type}\"`,\n\t\t\t\t\t\t\t\"INVALID_DATE_FIELD\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tnextType = input.type;\n\t\t\t\t\tupdates.type = input.type;\n\t\t\t\t\tupdates.column_type = newColumnType;\n\t\t\t\t}\n\n\t\t\t\tif (input.required !== undefined && input.required !== field.required) {\n\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t`Changing required for field \"${fieldSlug}\" requires a manual content migration.`,\n\t\t\t\t\t\t\"FIELD_UPDATE_REQUIRES_MIGRATION\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (input.unique !== undefined && input.unique !== field.unique) {\n\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t`Changing unique for field \"${fieldSlug}\" requires a manual content migration.`,\n\t\t\t\t\t\t\"FIELD_UPDATE_REQUIRES_MIGRATION\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (input.translatable === false && field.translatable) {\n\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t`Changing field \"${fieldSlug}\" to non-translatable requires a manual content migration.`,\n\t\t\t\t\t\t\"FIELD_UPDATE_REQUIRES_MIGRATION\",\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (input.label !== undefined) updates.label = input.label;\n\t\t\t\tif (input.required !== undefined) updates.required = input.required ? 1 : 0;\n\t\t\t\tif (input.unique !== undefined) updates.unique = input.unique ? 1 : 0;\n\t\t\t\tif (input.searchable !== undefined) updates.searchable = input.searchable ? 1 : 0;\n\t\t\t\tconst indexedChanged = input.indexed !== undefined && input.indexed !== field.indexed;\n\t\t\t\tif (indexedChanged) updates.indexed = input.indexed ? 1 : 0;\n\t\t\t\tif (input.translatable !== undefined) {\n\t\t\t\t\tupdates.translatable = input.translatable ? 1 : 0;\n\t\t\t\t}\n\t\t\t\tif (input.defaultValue !== undefined) {\n\t\t\t\t\tupdates.default_value = JSON.stringify(input.defaultValue);\n\t\t\t\t}\n\t\t\t\tif (input.validation !== undefined) {\n\t\t\t\t\tupdates.validation = input.validation ? JSON.stringify(input.validation) : null;\n\t\t\t\t}\n\t\t\t\tif (input.widget !== undefined) updates.widget = input.widget;\n\t\t\t\tif (input.options !== undefined) updates.options = JSON.stringify(input.options);\n\t\t\t\tif (input.sortOrder !== undefined) updates.sort_order = input.sortOrder;\n\n\t\t\t\tassertIndexableField(nextType, input.indexed ?? field.indexed, fieldSlug);\n\t\t\t\tif (Object.keys(updates).length === 0) return field;\n\n\t\t\t\tactiveCoverageInvalidated = await invalidateContentMediaUsageSchemaChange(\n\t\t\t\t\ttrx,\n\t\t\t\t\tcollectionSlug,\n\t\t\t\t);\n\n\t\t\t\tif (Object.keys(updates).length > 0) {\n\t\t\t\t\tawait trx.updateTable(\"_emdash_fields\").set(updates).where(\"id\", \"=\", field.id).execute();\n\t\t\t\t\tschemaMutated = true;\n\t\t\t\t}\n\n\t\t\t\tif (indexedChanged) {\n\t\t\t\t\tif (input.indexed) {\n\t\t\t\t\t\tawait this.createFieldIndex(collectionSlug, field.id, fieldSlug, trx);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait this.dropFieldIndex(field.id, trx);\n\t\t\t\t\t}\n\t\t\t\t\tschemaMutated = true;\n\t\t\t\t}\n\n\t\t\t\t// Read the updated field via trx (not this.db) to avoid connection mutex deadlock\n\t\t\t\tconst updatedRow = await trx\n\t\t\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t\t\t.where(\"collection_id\", \"=\", field.collectionId)\n\t\t\t\t\t.where(\"slug\", \"=\", fieldSlug)\n\t\t\t\t\t.selectAll()\n\t\t\t\t\t.executeTakeFirst();\n\n\t\t\t\tif (!updatedRow) {\n\t\t\t\t\tthrow new SchemaError(\"Failed to update field\", \"UPDATE_FAILED\");\n\t\t\t\t}\n\n\t\t\t\tconst updated = this.mapFieldRow(updatedRow);\n\n\t\t\t\t// If searchable changed, sync FTS state for this collection\n\t\t\t\tconst searchableChanged =\n\t\t\t\t\tschemaMutated && input.searchable !== undefined && input.searchable !== field.searchable;\n\t\t\t\tif (searchableChanged) {\n\t\t\t\t\tawait this.syncSearchState(collectionSlug, trx);\n\t\t\t\t}\n\n\t\t\t\treturn updated;\n\t\t\t});\n\t\t\tif (schemaMutated) {\n\t\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t\t} else {\n\t\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\t\tthis.db,\n\t\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.notifyTypegen();\n\t\t\treturn updatedField;\n\t\t} catch (error) {\n\t\t\tif (schemaMutated) {\n\t\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t\t} else {\n\t\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\t\tthis.db,\n\t\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Synchronize an existing FTS index with the collection's current state.\n\t *\n\t * Only rebuilds or disables — never first-time enables. First-time FTS\n\t * enablement is handled by the seed's explicit enableSearch call (which\n\t * is try-caught) or the admin UI toggle.\n\t *\n\t * - FTS active + still has search support and searchable fields → rebuild\n\t * - FTS active + lost search support or no searchable fields → disable\n\t * - FTS not active → no-op\n\t *\n\t * Pass `db` when calling from within a transaction so FTS operations\n\t * participate in the same transaction and are rolled back on failure.\n\t */\n\tprivate async syncSearchState(collectionSlug: string, db?: Kysely<Database>): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tconst ftsManager = new FTSManager(conn);\n\n\t\t// Query via conn (not this.db) to avoid connection mutex deadlock when called inside a transaction\n\t\tconst row = await conn\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.where(\"slug\", \"=\", collectionSlug)\n\t\t\t.select(\"supports\")\n\t\t\t.executeTakeFirst();\n\t\tif (!row) return;\n\n\t\tconst wantsSearch = parseSupports(row.supports).includes(\"search\");\n\t\tconst searchableFields = await ftsManager.getSearchableFields(collectionSlug);\n\t\tconst config = await ftsManager.getSearchConfig(collectionSlug);\n\t\tconst ftsActive = config?.enabled === true;\n\n\t\tif (wantsSearch && searchableFields.length > 0 && ftsActive) {\n\t\t\tawait ftsManager.rebuildIndex(\n\t\t\t\tcollectionSlug,\n\t\t\t\tsearchableFields,\n\t\t\t\tconfig?.weights,\n\t\t\t\tconfig?.tokenize,\n\t\t\t);\n\t\t} else if (ftsActive && (!wantsSearch || searchableFields.length === 0)) {\n\t\t\tawait ftsManager.disableSearch(collectionSlug);\n\t\t}\n\t}\n\n\t/**\n\t * Delete a field\n\t */\n\tasync deleteField(collectionSlug: string, fieldSlug: string): Promise<void> {\n\t\tconst field = await this.getField(collectionSlug, fieldSlug);\n\t\tif (!field) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Field \"${fieldSlug}\" not found in collection \"${collectionSlug}\"`,\n\t\t\t\t\"FIELD_NOT_FOUND\",\n\t\t\t);\n\t\t}\n\t\tconst activeCoverageInvalidated = await invalidateContentMediaUsageSchemaChange(\n\t\t\tthis.db,\n\t\t\tcollectionSlug,\n\t\t);\n\n\t\t// If this field powers the collection's titleField/dateField,\n\t\t// clear that reference in the same transaction — otherwise the metadata\n\t\t// would point at a dropped column and later crash the content list sort.\n\t\tconst collection = await this.getCollection(collectionSlug);\n\t\tconst clearTitle = collection?.titleField === fieldSlug;\n\t\tconst clearDate = collection?.dateField === fieldSlug;\n\n\t\tlet schemaMutated = false;\n\t\ttry {\n\t\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\t\t// Delete the field record first so syncSearchState sees the updated field list.\n\t\t\t\t// This ordering matters for searchable fields: SQLite prevents dropping a column\n\t\t\t\t// that is still referenced by a trigger. syncSearchState drops and recreates the\n\t\t\t\t// FTS triggers based on the remaining searchable fields, clearing the dependency\n\t\t\t\t// before we attempt the ALTER TABLE DROP COLUMN below.\n\t\t\t\tawait trx.deleteFrom(\"_emdash_fields\").where(\"id\", \"=\", field.id).execute();\n\t\t\t\tschemaMutated = true;\n\n\t\t\t\tif (clearTitle || clearDate) {\n\t\t\t\t\tawait trx\n\t\t\t\t\t\t.updateTable(\"_emdash_collections\")\n\t\t\t\t\t\t.set({\n\t\t\t\t\t\t\t...(clearTitle ? { title_field: null } : {}),\n\t\t\t\t\t\t\t...(clearDate ? { date_field: null } : {}),\n\t\t\t\t\t\t\tupdated_at: new Date().toISOString(),\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.where(\"slug\", \"=\", collectionSlug)\n\t\t\t\t\t\t.execute();\n\t\t\t\t}\n\n\t\t\t\t// If the deleted field was searchable, sync FTS state (removes old triggers)\n\t\t\t\tif (field.searchable) {\n\t\t\t\t\tawait this.syncSearchState(collectionSlug, trx);\n\t\t\t\t}\n\n\t\t\t\tif (field.indexed) {\n\t\t\t\t\tawait this.dropFieldIndex(field.id, trx);\n\t\t\t\t}\n\n\t\t\t\t// Drop column from content table — safe now because FTS triggers are gone\n\t\t\t\tawait this.dropColumn(collectionSlug, fieldSlug, trx);\n\t\t\t});\n\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t} else {\n\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\tthis.db,\n\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (schemaMutated) {\n\t\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t\t} else {\n\t\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\t\tthis.db,\n\t\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tthis.notifyTypegen();\n\t}\n\n\t/**\n\t * Reorder collections in the admin sidebar.\n\t *\n\t * `slugs` is the full desired order: every listed collection gets its\n\t * index as `sort_order`, and any collection left out has its explicit\n\t * position cleared, dropping it back to the alphabetical tail. Unknown or\n\t * duplicate slugs throw before anything is written.\n\t */\n\tasync reorderCollections(slugs: string[]): Promise<void> {\n\t\tconst known = new Set((await this.listCollections()).map((collection) => collection.slug));\n\n\t\tconst unknown = slugs.filter((slug) => !known.has(slug));\n\t\tif (unknown.length > 0) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Unknown collection${unknown.length > 1 ? \"s\" : \"\"}: ${unknown.join(\", \")}`,\n\t\t\t\t\"COLLECTION_NOT_FOUND\",\n\t\t\t\t{ slugs: unknown },\n\t\t\t);\n\t\t}\n\n\t\tconst duplicates = slugs.filter((slug, index) => slugs.indexOf(slug) !== index);\n\t\tif (duplicates.length > 0) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Duplicate collection${duplicates.length > 1 ? \"s\" : \"\"}: ${[...new Set(duplicates)].join(\", \")}`,\n\t\t\t\t\"DUPLICATE_SLUG\",\n\t\t\t\t{ slugs: [...new Set(duplicates)] },\n\t\t\t);\n\t\t}\n\n\t\tconst now = new Date().toISOString();\n\t\tconst ordered = new Set(slugs);\n\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tfor (const [index, slug] of slugs.entries()) {\n\t\t\t\tawait trx\n\t\t\t\t\t.updateTable(\"_emdash_collections\")\n\t\t\t\t\t.set({ sort_order: index, updated_at: now })\n\t\t\t\t\t.where(\"slug\", \"=\", slug)\n\t\t\t\t\t.execute();\n\t\t\t}\n\n\t\t\tconst cleared = [...known].filter((slug) => !ordered.has(slug));\n\t\t\t// Chunked to stay under D1's bound-parameter limit; typical sites\n\t\t\t// clear far fewer than one chunk.\n\t\t\tfor (const slugChunk of chunks(cleared, SQL_BATCH_SIZE)) {\n\t\t\t\tawait trx\n\t\t\t\t\t.updateTable(\"_emdash_collections\")\n\t\t\t\t\t.set({ sort_order: null, updated_at: now })\n\t\t\t\t\t.where(\"slug\", \"in\", slugChunk)\n\t\t\t\t\t.execute();\n\t\t\t}\n\t\t});\n\t\tthis.notifyTypegen();\n\t}\n\n\t/**\n\t * Reorder fields\n\t */\n\tasync reorderFields(collectionSlug: string, fieldSlugs: string[]): Promise<void> {\n\t\tconst collection = await this.getCollection(collectionSlug);\n\t\tif (!collection) {\n\t\t\tthrow new SchemaError(`Collection \"${collectionSlug}\" not found`, \"COLLECTION_NOT_FOUND\");\n\t\t}\n\n\t\t// Update sort_order for each field\n\t\tfor (let i = 0; i < fieldSlugs.length; i++) {\n\t\t\tawait this.db\n\t\t\t\t.updateTable(\"_emdash_fields\")\n\t\t\t\t.set({ sort_order: i })\n\t\t\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t\t\t.where(\"slug\", \"=\", fieldSlugs[i])\n\t\t\t\t.execute();\n\t\t}\n\t\tthis.notifyTypegen();\n\t}\n\n\t// ============================================\n\t// DDL Operations\n\t// ============================================\n\n\t/**\n\t * Create a content table for a collection\n\t */\n\tprivate async createContentTable(\n\t\tslug: string,\n\t\tdb?: Kysely<Database>,\n\t\tfields: readonly CreateFieldInput[] = [],\n\t\toptions: { ifNotExists?: boolean } = {},\n\t): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tconst tableName = this.getTableName(slug);\n\n\t\tlet table: CreateTableBuilder<string, string> = conn.schema\n\t\t\t.createTable(tableName)\n\t\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t\t.addColumn(\"slug\", \"text\")\n\t\t\t.addColumn(\"status\", \"text\", (col) => col.defaultTo(\"draft\"))\n\t\t\t.addColumn(\"author_id\", \"text\")\n\t\t\t.addColumn(\"primary_byline_id\", \"text\")\n\t\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(conn)))\n\t\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(conn)))\n\t\t\t.addColumn(\"published_at\", \"text\")\n\t\t\t.addColumn(\"scheduled_at\", \"text\")\n\t\t\t.addColumn(\"deleted_at\", \"text\")\n\t\t\t.addColumn(\"version\", \"integer\", (col) => col.defaultTo(1))\n\t\t\t.addColumn(\"live_revision_id\", \"text\", (col) => col.references(\"revisions.id\"))\n\t\t\t.addColumn(\"draft_revision_id\", \"text\", (col) => col.references(\"revisions.id\"))\n\t\t\t.addColumn(\"locale\", \"text\", (col) => col.notNull().defaultTo(\"en\"))\n\t\t\t.addColumn(\"translation_group\", \"text\");\n\t\tif (options.ifNotExists) table = table.ifNotExists();\n\n\t\tfor (const field of fields) {\n\t\t\tconst columnName = this.getColumnName(field.slug);\n\t\t\tconst columnType = COLUMN_TYPE_TO_DATA_TYPE[FIELD_TYPE_TO_COLUMN[field.type]];\n\t\t\ttable = table.addColumn(columnName, columnType, (column) => {\n\t\t\t\tif (!field.required) return column;\n\n\t\t\t\tconst defaultValue =\n\t\t\t\t\tfield.defaultValue !== undefined\n\t\t\t\t\t\t? this.formatDefaultValue(field.defaultValue, field.type)\n\t\t\t\t\t\t: this.getEmptyDefault(field.type);\n\t\t\t\treturn column.notNull().defaultTo(sql.raw(defaultValue));\n\t\t\t});\n\t\t}\n\n\t\tawait table\n\t\t\t.addUniqueConstraint(`${tableName}_slug_locale_unique`, [\"slug\", \"locale\"])\n\t\t\t.execute();\n\n\t\tconst createIndex = options.ifNotExists ? sql`CREATE INDEX IF NOT EXISTS` : sql`CREATE INDEX`;\n\n\t\t// Create standard indexes\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_slug`)}\n\t\t\tON ${sql.ref(tableName)} (slug)\n\t\t`.execute(conn);\n\n\t\t// Name must stay identical to migration 074. `deleted_at` leads so the\n\t\t// scheduled-publishing sweep can seek it and walk `scheduled_at` for both\n\t\t// its range and its ORDER BY; a `scheduled_at`-only index leaves a\n\t\t// stats-blind planner reading every live row.\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_del_sched`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, scheduled_at)\n\t\t\tWHERE scheduled_at IS NOT NULL\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_live_revision`)}\n\t\t\tON ${sql.ref(tableName)} (live_revision_id)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_draft_revision`)}\n\t\t\tON ${sql.ref(tableName)} (draft_revision_id)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_author`)}\n\t\t\tON ${sql.ref(tableName)} (author_id)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_primary_byline`)}\n\t\t\tON ${sql.ref(tableName)} (primary_byline_id)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_locale`)}\n\t\t\tON ${sql.ref(tableName)} (locale)\n\t\t`.execute(conn);\n\n\t\t// Names must stay identical to migration 055, which creates these indexes\n\t\t// on tables that already exist. Lookups that don't constrain `deleted_at`\n\t\t// (menu and reference resolution) need the first; reads that do need the\n\t\t// second.\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_tg_locale`)}\n\t\t\tON ${sql.ref(tableName)} (translation_group, locale)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_del_tg_locale`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, translation_group, locale)\n\t\t`.execute(conn);\n\n\t\t// Composite indexes for optimized query performance (see migration 033)\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_deleted_updated_id`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, updated_at DESC, id DESC)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_deleted_status`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, status)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_deleted_created_id`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, created_at DESC, id DESC)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_deleted_published_id`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, published_at DESC, id DESC)\n\t\t`.execute(conn);\n\n\t\t// Locale-aware composite indexes for i18n content lists (see migration 041).\n\t\t// Short `loc_upd`/`loc_crt` suffix keeps the updated/created discriminator\n\t\t// inside Postgres's 63-byte identifier limit for long slugs; keep these\n\t\t// names identical to migration 041.\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_loc_upd`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, locale, updated_at DESC, id DESC)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_loc_crt`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, locale, created_at DESC, id DESC)\n\t\t`.execute(conn);\n\t}\n\n\tprivate getFieldIndexName(fieldId: string): string {\n\t\tif (!FIELD_ID_PATTERN.test(fieldId)) {\n\t\t\tthrow new SchemaError(`Invalid field id \"${fieldId}\"`, \"INVALID_FIELD_ID\");\n\t\t}\n\t\treturn `idx_cf_${fieldId.toLowerCase()}`;\n\t}\n\n\tprivate getLocaleFieldIndexName(fieldId: string): string {\n\t\treturn `${this.getFieldIndexName(fieldId)}_loc`;\n\t}\n\n\tprivate async createFieldIndex(\n\t\tcollectionSlug: string,\n\t\tfieldId: string,\n\t\tfieldSlug: string,\n\t\tdb?: Kysely<Database>,\n\t): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tconst tableName = this.getTableName(collectionSlug);\n\t\tconst columnName = this.getColumnName(fieldSlug);\n\t\tconst indexName = this.getFieldIndexName(fieldId);\n\t\tconst localeIndexName = this.getLocaleFieldIndexName(fieldId);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(indexName)}\n\t\t\tON ${sql.ref(tableName)} (\n\t\t\t\t(${sql.ref(columnName)} IS NOT NULL),\n\t\t\t\t${sql.ref(columnName)},\n\t\t\t\tid\n\t\t\t)\n\t\t\tWHERE deleted_at IS NULL\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(localeIndexName)}\n\t\t\tON ${sql.ref(tableName)} (\n\t\t\t\tlocale,\n\t\t\t\t(${sql.ref(columnName)} IS NOT NULL),\n\t\t\t\t${sql.ref(columnName)},\n\t\t\t\tid\n\t\t\t)\n\t\t\tWHERE deleted_at IS NULL\n\t\t`.execute(conn);\n\t}\n\n\tprivate async dropFieldIndex(fieldId: string, db?: Kysely<Database>): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(this.getFieldIndexName(fieldId))}`.execute(conn);\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(this.getLocaleFieldIndexName(fieldId))}`.execute(conn);\n\t}\n\n\t/**\n\t * Add a column to a content table\n\t */\n\tprivate async addColumn(\n\t\tcollectionSlug: string,\n\t\tfieldSlug: string,\n\t\tfieldType: FieldType,\n\t\toptions?: { required?: boolean; defaultValue?: unknown },\n\t\tdb?: Kysely<Database>,\n\t): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tconst tableName = this.getTableName(collectionSlug);\n\t\tconst columnType = FIELD_TYPE_TO_COLUMN[fieldType];\n\t\tconst columnName = this.getColumnName(fieldSlug);\n\n\t\t// Build ALTER TABLE statement\n\t\t// Note: SQLite requires DEFAULT for NOT NULL columns in ALTER TABLE\n\t\tif (options?.required && options?.defaultValue !== undefined) {\n\t\t\tconst defaultVal = this.formatDefaultValue(options.defaultValue, fieldType);\n\t\t\tawait sql`\n\t\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\t\tADD COLUMN ${sql.ref(columnName)} ${sql.raw(columnType)} NOT NULL DEFAULT ${sql.raw(defaultVal)}\n\t\t\t`.execute(conn);\n\t\t} else if (options?.required) {\n\t\t\t// For required fields without default, use empty string/0 as default\n\t\t\tconst defaultVal = this.getEmptyDefault(fieldType);\n\t\t\tawait sql`\n\t\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\t\tADD COLUMN ${sql.ref(columnName)} ${sql.raw(columnType)} NOT NULL DEFAULT ${sql.raw(defaultVal)}\n\t\t\t`.execute(conn);\n\t\t} else {\n\t\t\tawait sql`\n\t\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\t\tADD COLUMN ${sql.ref(columnName)} ${sql.raw(columnType)}\n\t\t\t`.execute(conn);\n\t\t}\n\t}\n\n\t/**\n\t * Drop a column from a content table\n\t */\n\tprivate async dropColumn(\n\t\tcollectionSlug: string,\n\t\tfieldSlug: string,\n\t\tdb?: Kysely<Database>,\n\t): Promise<void> {\n\t\tconst tableName = this.getTableName(collectionSlug);\n\t\tconst columnName = this.getColumnName(fieldSlug);\n\n\t\tawait sql`\n\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\tDROP COLUMN ${sql.ref(columnName)}\n\t\t`.execute(db ?? this.db);\n\t}\n\n\t// ============================================\n\t// Helpers\n\t// ============================================\n\n\t/**\n\t * Check if a collection has any content\n\t */\n\tprivate async collectionHasContent(slug: string): Promise<boolean> {\n\t\tconst tableName = this.getTableName(slug);\n\t\ttry {\n\t\t\tconst result = await sql<{ count: number }>`\n\t\t\t\tSELECT COUNT(*) as count FROM ${sql.ref(tableName)}\n\t\t\t\tWHERE deleted_at IS NULL\n\t\t\t`.execute(this.db);\n\t\t\treturn (result.rows[0]?.count ?? 0) > 0;\n\t\t} catch {\n\t\t\t// Table might not exist\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Get table name for a collection\n\t */\n\tprivate getTableName(slug: string): string {\n\t\tvalidateIdentifier(slug, \"collection slug\");\n\t\treturn `ec_${slug}`;\n\t}\n\n\t/**\n\t * Get column name for a field\n\t */\n\tprivate getColumnName(slug: string): string {\n\t\tvalidateIdentifier(slug, \"field slug\");\n\t\treturn slug;\n\t}\n\n\t/**\n\t * Validate a slug\n\t */\n\tprivate validateSlug(slug: string, type: \"collection\" | \"field\"): void {\n\t\tif (!slug || typeof slug !== \"string\") {\n\t\t\tthrow new SchemaError(`${type} slug is required`, \"INVALID_SLUG\");\n\t\t}\n\n\t\tif (!SLUG_VALIDATION_PATTERN.test(slug)) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`${type} slug must start with a letter and contain only lowercase letters, numbers, and underscores`,\n\t\t\t\t\"INVALID_SLUG\",\n\t\t\t);\n\t\t}\n\n\t\tif (slug.length > 63) {\n\t\t\tthrow new SchemaError(`${type} slug must be 63 characters or less`, \"INVALID_SLUG\");\n\t\t}\n\t}\n\n\t/**\n\t * Format a default value for SQL.\n\t *\n\t * SQLite `ALTER TABLE ADD COLUMN ... DEFAULT` requires a literal constant\n\t * expression — parameterized values cannot be used here. We manually escape\n\t * single quotes and coerce types to ensure the output is safe.\n\t *\n\t * INTEGER/REAL values are coerced through `Number()` which can only produce\n\t * digits, `.`, `-`, `e`, `Infinity`, or `NaN` — all safe in SQL.\n\t * TEXT/JSON values have single quotes escaped via SQL standard doubling (`''`).\n\t */\n\tprivate formatDefaultValue(value: unknown, fieldType: FieldType): string {\n\t\tif (value === null || value === undefined) {\n\t\t\treturn \"NULL\";\n\t\t}\n\n\t\tconst columnType = FIELD_TYPE_TO_COLUMN[fieldType];\n\n\t\tif (columnType === \"JSON\") {\n\t\t\t// JSON.stringify produces valid JSON; escape single quotes for SQL literal\n\t\t\tconst json = JSON.stringify(value);\n\t\t\treturn `'${json.replace(SINGLE_QUOTE_PATTERN, \"''\")}'`;\n\t\t}\n\n\t\tif (columnType === \"INTEGER\") {\n\t\t\tif (typeof value === \"boolean\") {\n\t\t\t\treturn value ? \"1\" : \"0\";\n\t\t\t}\n\t\t\tconst num = Number(value);\n\t\t\tif (!Number.isFinite(num)) {\n\t\t\t\treturn \"0\";\n\t\t\t}\n\t\t\treturn String(Math.trunc(num));\n\t\t}\n\n\t\tif (columnType === \"REAL\") {\n\t\t\tconst num = Number(value);\n\t\t\tif (!Number.isFinite(num)) {\n\t\t\t\treturn \"0\";\n\t\t\t}\n\t\t\treturn String(num);\n\t\t}\n\n\t\t// TEXT — escape single quotes via SQL standard doubling\n\t\tlet text: string;\n\t\tif (typeof value === \"string\") {\n\t\t\ttext = value;\n\t\t} else if (typeof value === \"number\" || typeof value === \"boolean\") {\n\t\t\ttext = String(value);\n\t\t} else if (typeof value === \"object\" && value !== null) {\n\t\t\ttext = JSON.stringify(value);\n\t\t} else {\n\t\t\ttext = \"\";\n\t\t}\n\t\treturn `'${text.replace(SINGLE_QUOTE_PATTERN, \"''\")}'`;\n\t}\n\n\t/**\n\t * Get empty default for a field type\n\t */\n\tprivate getEmptyDefault(fieldType: FieldType): string {\n\t\tconst columnType = FIELD_TYPE_TO_COLUMN[fieldType];\n\n\t\tswitch (columnType) {\n\t\t\tcase \"INTEGER\":\n\t\t\t\treturn \"0\";\n\t\t\tcase \"REAL\":\n\t\t\t\treturn \"0.0\";\n\t\t\tcase \"JSON\":\n\t\t\t\treturn \"'null'\";\n\t\t\tdefault:\n\t\t\t\treturn \"''\";\n\t\t}\n\t}\n\n\t/**\n\t * Map a collection row to a Collection object\n\t */\n\tprivate mapCollectionRow = (row: Selectable<CollectionTable>): Collection => {\n\t\tconst moderation = row.comments_moderation;\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tslug: row.slug,\n\t\t\tlabel: row.label,\n\t\t\tlabelSingular: row.label_singular ?? undefined,\n\t\t\tdescription: row.description ?? undefined,\n\t\t\ticon: row.icon ?? undefined,\n\t\t\tadmin: parseCollectionAdmin(row.admin_config),\n\t\t\tsupports: parseSupports(row.supports),\n\t\t\tsource: row.source && isCollectionSource(row.source) ? row.source : undefined,\n\t\t\thasSeo: row.has_seo === 1,\n\t\t\t// Raw value; undefined when unset. The admin list resolves the\n\t\t\t// default (title fallback chain / updatedAt) at the point of use.\n\t\t\ttitleField: row.title_field ?? undefined,\n\t\t\tdateField: row.date_field ?? undefined,\n\t\t\turlPattern: row.url_pattern ?? undefined,\n\t\t\troutable: row.routable !== 0,\n\t\t\thidden: row.hidden === 1,\n\t\t\tsortOrder: row.sort_order ?? undefined,\n\t\t\tcommentsEnabled: row.comments_enabled === 1,\n\t\t\tcommentsModeration:\n\t\t\t\tmoderation === \"all\" || moderation === \"first_time\" || moderation === \"none\"\n\t\t\t\t\t? moderation\n\t\t\t\t\t: \"first_time\",\n\t\t\tcommentsClosedAfterDays: row.comments_closed_after_days ?? 90,\n\t\t\tcommentsAutoApproveUsers: row.comments_auto_approve_users === 1,\n\t\t\tcreatedAt: row.created_at,\n\t\t\tupdatedAt: row.updated_at,\n\t\t};\n\t};\n\n\t/**\n\t * Map a field row to a Field object\n\t */\n\tprivate mapFieldRow = (row: Selectable<FieldTable>): Field => {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tcollectionId: row.collection_id,\n\t\t\tslug: row.slug,\n\t\t\tlabel: row.label,\n\t\t\ttype: isFieldType(row.type) ? row.type : \"string\",\n\t\t\tcolumnType: isColumnType(row.column_type) ? row.column_type : \"TEXT\",\n\t\t\trequired: row.required === 1,\n\t\t\tunique: row.unique === 1,\n\t\t\tdefaultValue: row.default_value ? JSON.parse(row.default_value) : undefined,\n\t\t\tvalidation: row.validation ? JSON.parse(row.validation) : undefined,\n\t\t\twidget: row.widget ?? undefined,\n\t\t\toptions: row.options ? JSON.parse(row.options) : undefined,\n\t\t\tsortOrder: row.sort_order,\n\t\t\tsearchable: row.searchable === 1,\n\t\t\tindexed: row.indexed === 1,\n\t\t\ttranslatable: row.translatable !== 0,\n\t\t\tcreatedAt: row.created_at,\n\t\t};\n\t};\n\n\t// ============================================\n\t// Discovery\n\t// ============================================\n\n\t/**\n\t * Discover orphaned content tables\n\t *\n\t * Finds ec_* tables that exist in the database but don't have a\n\t * corresponding entry in _emdash_collections.\n\t */\n\tasync discoverOrphanedTables(): Promise<\n\t\tArray<{ slug: string; tableName: string; rowCount: number }>\n\t> {\n\t\t// Get all ec_* tables\n\t\t// Content tables are ec_* (e.g., ec_posts, ec_pages)\n\t\t// Internal tables are _emdash_* (e.g., _emdash_collections, _emdash_fts_posts)\n\t\tconst allTables = await listTablesLike(this.db, \"ec_%\");\n\n\t\t// Get registered collections\n\t\tconst registered = await this.listCollections();\n\t\tconst registeredSlugs = new Set(registered.map((c) => c.slug));\n\n\t\t// Find orphans\n\t\tconst orphans: Array<{\n\t\t\tslug: string;\n\t\t\ttableName: string;\n\t\t\trowCount: number;\n\t\t}> = [];\n\n\t\tfor (const tableName of allTables) {\n\t\t\tconst slug = tableName.replace(EC_PREFIX_PATTERN, \"\");\n\n\t\t\tif (!registeredSlugs.has(slug)) {\n\t\t\t\t// Count rows in the orphaned table\n\t\t\t\ttry {\n\t\t\t\t\tconst countResult = await sql<{ count: number }>`\n\t\t\t\t\t\tSELECT COUNT(*) as count FROM ${sql.ref(tableName)}\n\t\t\t\t\t\tWHERE deleted_at IS NULL\n\t\t\t\t\t`.execute(this.db);\n\n\t\t\t\t\torphans.push({\n\t\t\t\t\t\tslug,\n\t\t\t\t\t\ttableName,\n\t\t\t\t\t\trowCount: countResult.rows[0]?.count ?? 0,\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\t// Table might have unexpected schema, still report it\n\t\t\t\t\torphans.push({\n\t\t\t\t\t\tslug,\n\t\t\t\t\t\ttableName,\n\t\t\t\t\t\trowCount: 0,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn orphans;\n\t}\n\n\t/**\n\t * Register an orphaned table as a collection\n\t *\n\t * Creates a _emdash_collections entry for an existing ec_* table.\n\t */\n\tasync registerOrphanedTable(\n\t\tslug: string,\n\t\toptions?: {\n\t\t\tlabel?: string;\n\t\t\tlabelSingular?: string;\n\t\t\tdescription?: string;\n\t\t},\n\t): Promise<Collection> {\n\t\t// Verify table exists\n\t\tconst tableName = this.getTableName(slug);\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" is already registered`, \"COLLECTION_EXISTS\");\n\t\t}\n\t\tconst exists = await tableExists(this.db, tableName);\n\n\t\tif (!exists) {\n\t\t\tthrow new SchemaError(`Table \"${tableName}\" does not exist`, \"TABLE_NOT_FOUND\");\n\t\t}\n\n\t\t// Check if already registered\n\t\tconst existing = await this.getCollection(slug);\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" is already registered`, \"COLLECTION_EXISTS\");\n\t\t}\n\t\tif (\n\t\t\texisting &&\n\t\t\t!(await canResumeMediaUsageCollectionCapture(this.db, {\n\t\t\t\tcollectionId: existing.id,\n\t\t\t\tcollectionSlug: existing.slug,\n\t\t\t}))\n\t\t) {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" is already registered`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\t// Create collection entry\n\t\tconst proposedId = existing?.id ?? ulid();\n\t\tconst label = options?.label || this.slugToLabel(slug);\n\n\t\tlet collectionRegistered = false;\n\t\ttry {\n\t\t\tconst capture = await prepareMediaUsageCollectionCapture(this.db, {\n\t\t\t\tcollectionId: proposedId,\n\t\t\t\tcollectionSlug: slug,\n\t\t\t\tregisteredCollectionId: existing?.id,\n\t\t\t});\n\t\t\tif (capture.captureRequired) {\n\t\t\t\tawait installPreparedMediaUsageCollectionCapture(this.db, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: slug,\n\t\t\t\t});\n\t\t\t\tawait markMediaUsageCollectionCaptureReady(this.db, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: slug,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (!capture.registrationExists) {\n\t\t\t\tawait this.db\n\t\t\t\t\t.insertInto(\"_emdash_collections\")\n\t\t\t\t\t.values({\n\t\t\t\t\t\tid: capture.collectionId,\n\t\t\t\t\t\tslug,\n\t\t\t\t\t\tlabel,\n\t\t\t\t\t\tlabel_singular: options?.labelSingular ?? null,\n\t\t\t\t\t\tdescription: options?.description ?? null,\n\t\t\t\t\t\ticon: null,\n\t\t\t\t\t\tsupports: JSON.stringify([]),\n\t\t\t\t\t\tsource: \"discovered\",\n\t\t\t\t\t\thas_seo: 0,\n\t\t\t\t\t\turl_pattern: null,\n\t\t\t\t\t})\n\t\t\t\t\t.execute();\n\t\t\t\tcollectionRegistered = true;\n\t\t\t}\n\t\t\tif (capture.captureRequired) {\n\t\t\t\tawait finalizeMediaUsageCollectionCapture(this.db, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: slug,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst collection = await this.getCollection(slug);\n\t\t\tif (!collection) {\n\t\t\t\tthrow new SchemaError(\"Failed to register orphaned table\", \"REGISTER_FAILED\");\n\t\t\t}\n\t\t\tawait markContentMediaUsageCollectionStaleSafely(this.db, slug, \"CONTENT_USAGE_STALE\");\n\t\t\tthis.notifyTypegen();\n\n\t\t\treturn collection;\n\t\t} catch (error) {\n\t\t\tif (collectionRegistered) {\n\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(this.db, slug, \"CONTENT_USAGE_STALE\");\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Convert slug to human-readable label\n\t */\n\tprivate slugToLabel(slug: string): string {\n\t\treturn slug\n\t\t\t.replace(UNDERSCORE_PATTERN, \" \")\n\t\t\t.replace(WORD_BOUNDARY_PATTERN, (c) => c.toUpperCase());\n\t}\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-fiZ8G9RJ.mjs";
1
+ import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-C4szrD8o.mjs";
2
2
  import { i as InvalidCursorError, o as decodeCursor, s as encodeCursor } from "./types-CQPWhPxE.mjs";
3
3
  import { ulid } from "ulidx";
4
4
 
@@ -331,4 +331,4 @@ var RelationRepository = class {
331
331
 
332
332
  //#endregion
333
333
  export { RelationRepository as t };
334
- //# sourceMappingURL=relation-BnHB0T4J.mjs.map
334
+ //# sourceMappingURL=relation-04DfKRTO.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"relation-BnHB0T4J.mjs","names":[],"sources":["../src/database/repositories/relation.ts"],"sourcesContent":["import type { Kysely, Selectable } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { chunks, SQL_BATCH_SIZE } from \"../../utils/chunks.js\";\nimport type { Database, RelationTable, ContentReferenceTable } from \"../types.js\";\nimport { decodeCursor, encodeCursor, InvalidCursorError, type FindManyResult } from \"./types.js\";\n\nexport interface Relation {\n\tid: string;\n\tname: string;\n\tparentCollection: string;\n\tchildCollection: string;\n\tparentLabel: string;\n\tchildLabel: string;\n\tlocale: string;\n\ttranslationGroup: string;\n}\n\nexport interface CreateRelationInput {\n\tname: string;\n\t/** Required for a base relation; ignored (inherited from the source) when\n\t * `translationOf` is set. */\n\tparentCollection?: string;\n\t/** Required for a base relation; ignored (inherited from the source) when\n\t * `translationOf` is set. */\n\tchildCollection?: string;\n\tparentLabel: string;\n\tchildLabel: string;\n\t/** Omit to let the DB default (current value: 'en') apply. Higher layers\n\t * resolve locale from request context / i18n config. */\n\tlocale?: string;\n\t/** When set, joins the source relation's translation_group AND inherits its\n\t * structural fields (name, parentCollection, childCollection). Only locale +\n\t * labels may differ on a translation. */\n\ttranslationOf?: string;\n}\n\nexport interface UpdateRelationInput {\n\t/** Only localized fields are mutable per row. Changing structural fields\n\t * (name/collections) is a cross-group operation deferred to a later slice. */\n\tparentLabel?: string;\n\tchildLabel?: string;\n}\n\nexport interface ContentReference {\n\tid: string;\n\trelationGroup: string;\n\tparentGroup: string;\n\tchildGroup: string;\n\tsortOrder: number;\n}\n\n/**\n * Content-references repository.\n *\n * Owns relation *definitions* (`_emdash_relations`, row-per-locale, mirroring\n * `_emdash_taxonomy_defs`) and the *edge* junction (`_emdash_content_references`,\n * keyed by `translation_group` so edges are locale-agnostic, mirroring\n * `content_taxonomies`).\n *\n * Like `TaxonomyRepository`, this is not the validation boundary: it trusts its\n * typed inputs. The API slice supplies Zod schemas at the route and enforces\n * collection-agreement / relation-existence invariants in the handler. The repo\n * does not resolve locale fallbacks — callers pass the locale they want.\n */\nexport class RelationRepository {\n\tconstructor(private db: Kysely<Database>) {}\n\n\t/**\n\t * Create a relation. Without `translationOf`, mints a fresh group\n\t * (`translation_group = id`, matching the migration backfill pattern). With\n\t * `translationOf`, the structural fields (name, parentCollection,\n\t * childCollection) and the translation_group are inherited from the source;\n\t * locale and the two labels are taken from `input`.\n\t */\n\tasync create(input: CreateRelationInput): Promise<Relation> {\n\t\tconst id = ulid();\n\t\tconst now = new Date().toISOString();\n\n\t\tlet translationGroup = id;\n\t\tlet name: string;\n\t\tlet parentCollection: string;\n\t\tlet childCollection: string;\n\n\t\tif (input.translationOf) {\n\t\t\tconst source = await this.findById(input.translationOf);\n\t\t\t// translation_group is NOT NULL here, so we cannot fall back to a\n\t\t\t// fresh group like TaxonomyRepository does — a bad translationOf must\n\t\t\t// fail loudly rather than silently mint an unlinked relation.\n\t\t\tif (!source) throw new Error(\"Source relation for translation not found\");\n\t\t\ttranslationGroup = source.translationGroup;\n\t\t\tname = source.name;\n\t\t\tparentCollection = source.parentCollection;\n\t\t\tchildCollection = source.childCollection;\n\t\t} else {\n\t\t\t// A base relation carries its own structural fields. The API layer's Zod\n\t\t\t// schema enforces this; guard here too since the repo trusts its inputs\n\t\t\t// and the columns are NOT NULL.\n\t\t\tif (input.parentCollection === undefined || input.childCollection === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"parentCollection and childCollection are required unless translationOf is set\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tname = input.name;\n\t\t\tparentCollection = input.parentCollection;\n\t\t\tchildCollection = input.childCollection;\n\t\t}\n\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_relations\")\n\t\t\t.values({\n\t\t\t\tid,\n\t\t\t\tname,\n\t\t\t\tparent_collection: parentCollection,\n\t\t\t\tchild_collection: childCollection,\n\t\t\t\tparent_label: input.parentLabel,\n\t\t\t\tchild_label: input.childLabel,\n\t\t\t\tcreated_at: now,\n\t\t\t\tupdated_at: now,\n\t\t\t\t// Omit `locale` so the DB DEFAULT (configured defaultLocale)\n\t\t\t\t// applies — matches TaxonomyRepository.create.\n\t\t\t\t...(input.locale !== undefined ? { locale: input.locale } : {}),\n\t\t\t\ttranslation_group: translationGroup,\n\t\t\t})\n\t\t\t.execute();\n\n\t\tconst relation = await this.findById(id);\n\t\tif (!relation) throw new Error(\"Failed to create relation\");\n\t\treturn relation;\n\t}\n\n\tasync findById(id: string): Promise<Relation | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_relations\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\t\treturn row ? this.rowToRelation(row) : null;\n\t}\n\n\t/**\n\t * Find a relation by name. With `locale`, filter by it; without, return the\n\t * lowest-locale-code match deterministically. Mirrors\n\t * `TaxonomyRepository.findBySlug` — note this returns a single row, unlike\n\t * `TaxonomyRepository.findByName` which returns every term in a taxonomy.\n\t */\n\tasync findByName(name: string, locale?: string): Promise<Relation | null> {\n\t\tlet query = this.db.selectFrom(\"_emdash_relations\").selectAll().where(\"name\", \"=\", name);\n\t\tif (locale !== undefined) query = query.where(\"locale\", \"=\", locale);\n\t\tconst row = await query.orderBy(\"locale\", \"asc\").executeTakeFirst();\n\t\treturn row ? this.rowToRelation(row) : null;\n\t}\n\n\t/** Every translation sibling (including itself) sharing a translation_group. */\n\tasync findTranslations(translationGroup: string): Promise<Relation[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_relations\")\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 rows.map((row) => this.rowToRelation(row));\n\t}\n\n\t/**\n\t * All relations, ordered by name then id (id is a stable tiebreak for\n\t * relations sharing a name across locales). Optionally filtered by locale.\n\t */\n\tasync list(locale?: string): Promise<Relation[]> {\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_relations\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(\"name\", \"asc\")\n\t\t\t.orderBy(\"id\", \"asc\");\n\t\tif (locale !== undefined) query = query.where(\"locale\", \"=\", locale);\n\t\tconst rows = await query.execute();\n\t\treturn rows.map((row) => this.rowToRelation(row));\n\t}\n\n\t/** Relations where `collection` is the parent OR the child side. */\n\tasync findForCollection(collection: string, locale?: string): Promise<Relation[]> {\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_relations\")\n\t\t\t.selectAll()\n\t\t\t.where((eb) =>\n\t\t\t\teb.or([eb(\"parent_collection\", \"=\", collection), eb(\"child_collection\", \"=\", collection)]),\n\t\t\t)\n\t\t\t.orderBy(\"name\", \"asc\")\n\t\t\t.orderBy(\"id\", \"asc\");\n\t\tif (locale !== undefined) query = query.where(\"locale\", \"=\", locale);\n\t\tconst rows = await query.execute();\n\t\treturn rows.map((row) => this.rowToRelation(row));\n\t}\n\n\t/**\n\t * Update the localized labels of one relation row. Structural fields are\n\t * immutable here (a cross-group concern). No-ops when nothing is supplied.\n\t */\n\tasync update(id: string, input: UpdateRelationInput): Promise<Relation | null> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) return null;\n\n\t\tconst updates: Record<string, unknown> = {};\n\t\tif (input.parentLabel !== undefined) updates.parent_label = input.parentLabel;\n\t\tif (input.childLabel !== undefined) updates.child_label = input.childLabel;\n\n\t\tif (Object.keys(updates).length > 0) {\n\t\t\tupdates.updated_at = new Date().toISOString();\n\t\t\tawait this.db.updateTable(\"_emdash_relations\").set(updates).where(\"id\", \"=\", id).execute();\n\t\t}\n\n\t\treturn this.findById(id);\n\t}\n\n\t/**\n\t * Delete one relation row. When it is the *last* translation of its group,\n\t * purge edges referencing that group (application-layer cascade — group\n\t * linking precludes a SQL FK). Mirrors `TaxonomyRepository.delete`.\n\t */\n\tasync delete(id: string): Promise<boolean> {\n\t\tconst relation = await this.findById(id);\n\t\tif (!relation) return false;\n\n\t\tconst siblings = await this.db\n\t\t\t.selectFrom(\"_emdash_relations\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"translation_group\", \"=\", relation.translationGroup)\n\t\t\t.where(\"id\", \"!=\", id)\n\t\t\t.execute();\n\t\tif (siblings.length === 0) {\n\t\t\tawait this.db\n\t\t\t\t.deleteFrom(\"_emdash_content_references\")\n\t\t\t\t.where(\"relation_group\", \"=\", relation.translationGroup)\n\t\t\t\t.execute();\n\t\t}\n\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"_emdash_relations\")\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\t\treturn (result.numDeletedRows ?? 0n) > 0n;\n\t}\n\n\t/** Normalize a relation id OR group to its translation_group. Returns null\n\t * for an unknown relation (edge methods then no-op, matching\n\t * `TaxonomyRepository.attachToEntry`). */\n\tprivate async resolveRelationGroup(idOrGroup: string): Promise<string | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_relations\")\n\t\t\t.select([\"translation_group\"])\n\t\t\t.where((eb) => eb.or([eb(\"id\", \"=\", idOrGroup), eb(\"translation_group\", \"=\", idOrGroup)]))\n\t\t\t.executeTakeFirst();\n\t\treturn row?.translation_group ?? null;\n\t}\n\n\tprivate rowToReference(row: Selectable<ContentReferenceTable>): ContentReference {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\trelationGroup: row.relation_group,\n\t\t\tparentGroup: row.parent_group,\n\t\t\tchildGroup: row.child_group,\n\t\t\tsortOrder: row.sort_order,\n\t\t};\n\t}\n\n\t/**\n\t * Link `parentGroup → childGroup` under a relation. `relation` is a relation\n\t * id or group. Idempotent (onConflict doNothing against the unique edge).\n\t * `sortOrder` defaults to append: max(sort_order)+1 within (relation, parent).\n\t *\n\t * The default-append MAX→INSERT is not atomic: concurrent appends without an\n\t * explicit `sortOrder` may both read the same max and collide on sort_order,\n\t * and onConflict silently drops the loser. Callers needing strict ordering\n\t * under concurrency should pass `sortOrder` explicitly (or serialize).\n\t */\n\tasync addReference(\n\t\trelation: string,\n\t\tparentGroup: string,\n\t\tchildGroup: string,\n\t\tsortOrder?: number,\n\t): Promise<void> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return;\n\n\t\tlet order = sortOrder;\n\t\tif (order === undefined) {\n\t\t\tconst max = await this.db\n\t\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t\t.select((eb) => eb.fn.max(\"sort_order\").as(\"max\"))\n\t\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t\t.where(\"parent_group\", \"=\", parentGroup)\n\t\t\t\t.executeTakeFirst();\n\t\t\torder = max?.max === null || max?.max === undefined ? 0 : Number(max.max) + 1;\n\t\t}\n\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_content_references\")\n\t\t\t.values({\n\t\t\t\tid: ulid(),\n\t\t\t\trelation_group: relationGroup,\n\t\t\t\tparent_group: parentGroup,\n\t\t\t\tchild_group: childGroup,\n\t\t\t\tsort_order: order,\n\t\t\t\tcreated_at: new Date().toISOString(),\n\t\t\t})\n\t\t\t.onConflict((oc) => oc.doNothing())\n\t\t\t.execute();\n\t}\n\n\t/** Remove one `parentGroup → childGroup` edge under a relation. */\n\tasync removeReference(relation: string, parentGroup: string, childGroup: string): Promise<void> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return;\n\n\t\tawait this.db\n\t\t\t.deleteFrom(\"_emdash_content_references\")\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"parent_group\", \"=\", parentGroup)\n\t\t\t.where(\"child_group\", \"=\", childGroup)\n\t\t\t.execute();\n\t}\n\n\t/** Forward traversal: a parent's children for a relation, ordered. */\n\tasync getChildren(relation: string, parentGroup: string): Promise<ContentReference[]> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return [];\n\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t.selectAll()\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"parent_group\", \"=\", parentGroup)\n\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t.orderBy(\"id\", \"asc\")\n\t\t\t.execute();\n\t\treturn rows.map((row) => this.rowToReference(row));\n\t}\n\n\t/**\n\t * Forward traversal, paginated: one page of a parent's children for a\n\t * relation, ordered by `(sort_order, id)`. Use this on request paths — a\n\t * parent's children are capped but still up to 1000, and an unbounded read\n\t * scales poorly. Returns `{ items, nextCursor? }`; the cursor's order value is\n\t * the row's `sort_order`. Default limit 50, max 100.\n\t */\n\tasync getChildrenPage(\n\t\trelation: string,\n\t\tparentGroup: string,\n\t\toptions: { limit?: number; cursor?: string } = {},\n\t): Promise<FindManyResult<ContentReference>> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return { items: [] };\n\n\t\tconst limit = Math.min(options.limit || 50, 100);\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t.selectAll()\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"parent_group\", \"=\", parentGroup);\n\n\t\tif (options.cursor) {\n\t\t\tconst decoded = decodeCursor(options.cursor);\n\t\t\tconst sortOrder = Number(decoded.orderValue);\n\t\t\t// `decodeCursor` only guarantees `orderValue` is a string; a hand-crafted\n\t\t\t// cursor with a non-numeric order value would coerce to NaN and blow up at\n\t\t\t// the driver bind as a 500. A bad cursor is a client error — surface it as\n\t\t\t// INVALID_CURSOR (400). Server-issued cursors are always numeric here.\n\t\t\tif (!Number.isFinite(sortOrder)) throw new InvalidCursorError(options.cursor);\n\t\t\tquery = query.where((eb) =>\n\t\t\t\teb.or([\n\t\t\t\t\teb(\"sort_order\", \">\", sortOrder),\n\t\t\t\t\teb.and([eb(\"sort_order\", \"=\", sortOrder), eb(\"id\", \">\", decoded.id)]),\n\t\t\t\t]),\n\t\t\t);\n\t\t}\n\n\t\tconst rows = await query\n\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t.orderBy(\"id\", \"asc\")\n\t\t\t.limit(limit + 1)\n\t\t\t.execute();\n\n\t\tconst hasMore = rows.length > limit;\n\t\tconst items = rows.slice(0, limit).map((row) => this.rowToReference(row));\n\t\tconst result: FindManyResult<ContentReference> = { items };\n\t\tconst last = items.at(-1);\n\t\tif (hasMore && last) {\n\t\t\tresult.nextCursor = encodeCursor(String(last.sortOrder), last.id);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/** Backlink traversal: the parents that reference a child for a relation. */\n\tasync getParents(relation: string, childGroup: string): Promise<ContentReference[]> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return [];\n\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t.selectAll()\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"child_group\", \"=\", childGroup)\n\t\t\t.orderBy(\"id\", \"asc\")\n\t\t\t.execute();\n\t\treturn rows.map((row) => this.rowToReference(row));\n\t}\n\n\t/**\n\t * Replace all children of `parentGroup` under a relation with `childGroups`,\n\t * assigning positional sort_order (index in the deduped array). Deletes the\n\t * old set for this (relation, parent) and re-inserts — simple and correct;\n\t * the set is small (one parent's children). Mirrors the intent of\n\t * `TaxonomyRepository.setTermsForEntry`.\n\t *\n\t * A parent references a given child at most once (the unique edge), so\n\t * duplicate `childGroups` are collapsed first-occurrence-wins rather than\n\t * relying on the insert's onConflict to silently drop them. Not wrapped in a\n\t * transaction: a crash between the delete and insert leaves the parent with\n\t * no children — acceptable for a replace-all, since a retry restores state.\n\t *\n\t * Concurrency: two simultaneous replace-all calls for the same (relation,\n\t * parent) can interleave their deletes and inserts and merge into the union of\n\t * both sets (a lost update — neither \"replace\" wins). This is non-corrupting —\n\t * keyset pagination stays totally ordered via the `(sort_order, id)` tiebreak\n\t * even with duplicate sort_orders — and a single client editing one parent's\n\t * children serially never hits it. A D1-portable fix isn't available (no\n\t * multi-statement transactions), so concurrent replace-all on one parent is\n\t * unsupported by design rather than guarded here.\n\t */\n\tasync setChildren(relation: string, parentGroup: string, childGroups: string[]): Promise<void> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return;\n\n\t\tawait this.db\n\t\t\t.deleteFrom(\"_emdash_content_references\")\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"parent_group\", \"=\", parentGroup)\n\t\t\t.execute();\n\n\t\t// Collapse duplicates so positional sort_order has no gaps.\n\t\tconst uniqueChildGroups = [...new Set(childGroups)];\n\t\tif (uniqueChildGroups.length === 0) return;\n\n\t\tconst now = new Date().toISOString();\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_content_references\")\n\t\t\t.values(\n\t\t\t\tuniqueChildGroups.map((childGroup, index) => ({\n\t\t\t\t\tid: ulid(),\n\t\t\t\t\trelation_group: relationGroup,\n\t\t\t\t\tparent_group: parentGroup,\n\t\t\t\t\tchild_group: childGroup,\n\t\t\t\t\tsort_order: index,\n\t\t\t\t\tcreated_at: now,\n\t\t\t\t})),\n\t\t\t)\n\t\t\t// Belt-and-suspenders: the DELETE above already cleared this\n\t\t\t// (relation, parent), so no conflict is possible within one call.\n\t\t\t// This is NOT a concurrency guarantee — delete-then-insert is not atomic.\n\t\t\t.onConflict((oc) => oc.doNothing())\n\t\t\t.execute();\n\t}\n\n\t/**\n\t * Backlink traversal, paginated: one page of the parents that reference a\n\t * child for a relation, ordered by `id`. Unlike a parent's children, a\n\t * child's backlinks are *unbounded* — one popular entry can be referenced by\n\t * arbitrarily many parents — so this read must paginate. Returns\n\t * `{ items, nextCursor? }`; the cursor's order value is the row `id`. Default\n\t * limit 50, max 100.\n\t */\n\tasync getParentsPage(\n\t\trelation: string,\n\t\tchildGroup: string,\n\t\toptions: { limit?: number; cursor?: string } = {},\n\t): Promise<FindManyResult<ContentReference>> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return { items: [] };\n\n\t\tconst limit = Math.min(options.limit || 50, 100);\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t.selectAll()\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"child_group\", \"=\", childGroup);\n\n\t\tif (options.cursor) {\n\t\t\tconst decoded = decodeCursor(options.cursor);\n\t\t\tquery = query.where(\"id\", \">\", decoded.id);\n\t\t}\n\n\t\tconst rows = await query\n\t\t\t.orderBy(\"id\", \"asc\")\n\t\t\t.limit(limit + 1)\n\t\t\t.execute();\n\n\t\tconst hasMore = rows.length > limit;\n\t\tconst items = rows.slice(0, limit).map((row) => this.rowToReference(row));\n\t\tconst result: FindManyResult<ContentReference> = { items };\n\t\tconst last = items.at(-1);\n\t\tif (hasMore && last) {\n\t\t\tresult.nextCursor = encodeCursor(last.id, last.id);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Remove every edge where `group` is the parent OR the child — i.e. ensure no\n\t * orphaned reference edges survive when a content entry is deleted. The\n\t * application-layer cascade that group-linking precludes at the SQL level.\n\t * Wiring this into the content-delete path is a later (handler) slice.\n\t * Returns the number of edges removed.\n\t */\n\tasync clearReferencesForGroup(group: string): Promise<number> {\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"_emdash_content_references\")\n\t\t\t.where((eb) => eb.or([eb(\"parent_group\", \"=\", group), eb(\"child_group\", \"=\", group)]))\n\t\t\t.executeTakeFirst();\n\t\treturn Number(result.numDeletedRows ?? 0);\n\t}\n\n\t/** Count a parent's children under a relation. */\n\tasync countChildren(relation: string, parentGroup: string): Promise<number> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return 0;\n\t\tconst result = await this.db\n\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t.select((eb) => eb.fn.count(\"id\").as(\"count\"))\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"parent_group\", \"=\", parentGroup)\n\t\t\t.executeTakeFirst();\n\t\treturn Number(result?.count ?? 0);\n\t}\n\n\t/** Count a child's parents (backlinks) under a relation. */\n\tasync countParents(relation: string, childGroup: string): Promise<number> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return 0;\n\t\tconst result = await this.db\n\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t.select((eb) => eb.fn.count(\"id\").as(\"count\"))\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"child_group\", \"=\", childGroup)\n\t\t\t.executeTakeFirst();\n\t\treturn Number(result?.count ?? 0);\n\t}\n\n\t/**\n\t * Batch child-counts for many parents under a relation. Chunks at\n\t * SQL_BATCH_SIZE for D1's bind-parameter limit. Returns parent_group → count\n\t * (parents with no children are absent from the map). Mirrors\n\t * `TaxonomyRepository.countEntriesForTerms`.\n\t */\n\tasync countChildrenForParents(\n\t\trelation: string,\n\t\tparentGroups: string[],\n\t): Promise<Map<string, number>> {\n\t\tconst counts = new Map<string, number>();\n\t\tif (parentGroups.length === 0) return counts;\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return counts;\n\n\t\tfor (const chunk of chunks(parentGroups, SQL_BATCH_SIZE)) {\n\t\t\tconst rows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t\t.select([\"parent_group\", (eb) => eb.fn.count(\"id\").as(\"count\")])\n\t\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t\t.where(\"parent_group\", \"in\", chunk)\n\t\t\t\t.groupBy(\"parent_group\")\n\t\t\t\t.execute();\n\t\t\tfor (const row of rows) {\n\t\t\t\tcounts.set(row.parent_group, Number(row.count ?? 0));\n\t\t\t}\n\t\t}\n\t\treturn counts;\n\t}\n\n\tprivate rowToRelation(row: Selectable<RelationTable>): Relation {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tname: row.name,\n\t\t\tparentCollection: row.parent_collection,\n\t\t\tchildCollection: row.child_collection,\n\t\t\tparentLabel: row.parent_label,\n\t\t\tchildLabel: row.child_label,\n\t\t\tlocale: row.locale,\n\t\t\ttranslationGroup: row.translation_group,\n\t\t};\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"relation-04DfKRTO.mjs","names":[],"sources":["../src/database/repositories/relation.ts"],"sourcesContent":["import type { Kysely, Selectable } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { chunks, SQL_BATCH_SIZE } from \"../../utils/chunks.js\";\nimport type { Database, RelationTable, ContentReferenceTable } from \"../types.js\";\nimport { decodeCursor, encodeCursor, InvalidCursorError, type FindManyResult } from \"./types.js\";\n\nexport interface Relation {\n\tid: string;\n\tname: string;\n\tparentCollection: string;\n\tchildCollection: string;\n\tparentLabel: string;\n\tchildLabel: string;\n\tlocale: string;\n\ttranslationGroup: string;\n}\n\nexport interface CreateRelationInput {\n\tname: string;\n\t/** Required for a base relation; ignored (inherited from the source) when\n\t * `translationOf` is set. */\n\tparentCollection?: string;\n\t/** Required for a base relation; ignored (inherited from the source) when\n\t * `translationOf` is set. */\n\tchildCollection?: string;\n\tparentLabel: string;\n\tchildLabel: string;\n\t/** Omit to let the DB default (current value: 'en') apply. Higher layers\n\t * resolve locale from request context / i18n config. */\n\tlocale?: string;\n\t/** When set, joins the source relation's translation_group AND inherits its\n\t * structural fields (name, parentCollection, childCollection). Only locale +\n\t * labels may differ on a translation. */\n\ttranslationOf?: string;\n}\n\nexport interface UpdateRelationInput {\n\t/** Only localized fields are mutable per row. Changing structural fields\n\t * (name/collections) is a cross-group operation deferred to a later slice. */\n\tparentLabel?: string;\n\tchildLabel?: string;\n}\n\nexport interface ContentReference {\n\tid: string;\n\trelationGroup: string;\n\tparentGroup: string;\n\tchildGroup: string;\n\tsortOrder: number;\n}\n\n/**\n * Content-references repository.\n *\n * Owns relation *definitions* (`_emdash_relations`, row-per-locale, mirroring\n * `_emdash_taxonomy_defs`) and the *edge* junction (`_emdash_content_references`,\n * keyed by `translation_group` so edges are locale-agnostic, mirroring\n * `content_taxonomies`).\n *\n * Like `TaxonomyRepository`, this is not the validation boundary: it trusts its\n * typed inputs. The API slice supplies Zod schemas at the route and enforces\n * collection-agreement / relation-existence invariants in the handler. The repo\n * does not resolve locale fallbacks — callers pass the locale they want.\n */\nexport class RelationRepository {\n\tconstructor(private db: Kysely<Database>) {}\n\n\t/**\n\t * Create a relation. Without `translationOf`, mints a fresh group\n\t * (`translation_group = id`, matching the migration backfill pattern). With\n\t * `translationOf`, the structural fields (name, parentCollection,\n\t * childCollection) and the translation_group are inherited from the source;\n\t * locale and the two labels are taken from `input`.\n\t */\n\tasync create(input: CreateRelationInput): Promise<Relation> {\n\t\tconst id = ulid();\n\t\tconst now = new Date().toISOString();\n\n\t\tlet translationGroup = id;\n\t\tlet name: string;\n\t\tlet parentCollection: string;\n\t\tlet childCollection: string;\n\n\t\tif (input.translationOf) {\n\t\t\tconst source = await this.findById(input.translationOf);\n\t\t\t// translation_group is NOT NULL here, so we cannot fall back to a\n\t\t\t// fresh group like TaxonomyRepository does — a bad translationOf must\n\t\t\t// fail loudly rather than silently mint an unlinked relation.\n\t\t\tif (!source) throw new Error(\"Source relation for translation not found\");\n\t\t\ttranslationGroup = source.translationGroup;\n\t\t\tname = source.name;\n\t\t\tparentCollection = source.parentCollection;\n\t\t\tchildCollection = source.childCollection;\n\t\t} else {\n\t\t\t// A base relation carries its own structural fields. The API layer's Zod\n\t\t\t// schema enforces this; guard here too since the repo trusts its inputs\n\t\t\t// and the columns are NOT NULL.\n\t\t\tif (input.parentCollection === undefined || input.childCollection === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"parentCollection and childCollection are required unless translationOf is set\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tname = input.name;\n\t\t\tparentCollection = input.parentCollection;\n\t\t\tchildCollection = input.childCollection;\n\t\t}\n\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_relations\")\n\t\t\t.values({\n\t\t\t\tid,\n\t\t\t\tname,\n\t\t\t\tparent_collection: parentCollection,\n\t\t\t\tchild_collection: childCollection,\n\t\t\t\tparent_label: input.parentLabel,\n\t\t\t\tchild_label: input.childLabel,\n\t\t\t\tcreated_at: now,\n\t\t\t\tupdated_at: now,\n\t\t\t\t// Omit `locale` so the DB DEFAULT (configured defaultLocale)\n\t\t\t\t// applies — matches TaxonomyRepository.create.\n\t\t\t\t...(input.locale !== undefined ? { locale: input.locale } : {}),\n\t\t\t\ttranslation_group: translationGroup,\n\t\t\t})\n\t\t\t.execute();\n\n\t\tconst relation = await this.findById(id);\n\t\tif (!relation) throw new Error(\"Failed to create relation\");\n\t\treturn relation;\n\t}\n\n\tasync findById(id: string): Promise<Relation | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_relations\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\t\treturn row ? this.rowToRelation(row) : null;\n\t}\n\n\t/**\n\t * Find a relation by name. With `locale`, filter by it; without, return the\n\t * lowest-locale-code match deterministically. Mirrors\n\t * `TaxonomyRepository.findBySlug` — note this returns a single row, unlike\n\t * `TaxonomyRepository.findByName` which returns every term in a taxonomy.\n\t */\n\tasync findByName(name: string, locale?: string): Promise<Relation | null> {\n\t\tlet query = this.db.selectFrom(\"_emdash_relations\").selectAll().where(\"name\", \"=\", name);\n\t\tif (locale !== undefined) query = query.where(\"locale\", \"=\", locale);\n\t\tconst row = await query.orderBy(\"locale\", \"asc\").executeTakeFirst();\n\t\treturn row ? this.rowToRelation(row) : null;\n\t}\n\n\t/** Every translation sibling (including itself) sharing a translation_group. */\n\tasync findTranslations(translationGroup: string): Promise<Relation[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_relations\")\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 rows.map((row) => this.rowToRelation(row));\n\t}\n\n\t/**\n\t * All relations, ordered by name then id (id is a stable tiebreak for\n\t * relations sharing a name across locales). Optionally filtered by locale.\n\t */\n\tasync list(locale?: string): Promise<Relation[]> {\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_relations\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(\"name\", \"asc\")\n\t\t\t.orderBy(\"id\", \"asc\");\n\t\tif (locale !== undefined) query = query.where(\"locale\", \"=\", locale);\n\t\tconst rows = await query.execute();\n\t\treturn rows.map((row) => this.rowToRelation(row));\n\t}\n\n\t/** Relations where `collection` is the parent OR the child side. */\n\tasync findForCollection(collection: string, locale?: string): Promise<Relation[]> {\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_relations\")\n\t\t\t.selectAll()\n\t\t\t.where((eb) =>\n\t\t\t\teb.or([eb(\"parent_collection\", \"=\", collection), eb(\"child_collection\", \"=\", collection)]),\n\t\t\t)\n\t\t\t.orderBy(\"name\", \"asc\")\n\t\t\t.orderBy(\"id\", \"asc\");\n\t\tif (locale !== undefined) query = query.where(\"locale\", \"=\", locale);\n\t\tconst rows = await query.execute();\n\t\treturn rows.map((row) => this.rowToRelation(row));\n\t}\n\n\t/**\n\t * Update the localized labels of one relation row. Structural fields are\n\t * immutable here (a cross-group concern). No-ops when nothing is supplied.\n\t */\n\tasync update(id: string, input: UpdateRelationInput): Promise<Relation | null> {\n\t\tconst existing = await this.findById(id);\n\t\tif (!existing) return null;\n\n\t\tconst updates: Record<string, unknown> = {};\n\t\tif (input.parentLabel !== undefined) updates.parent_label = input.parentLabel;\n\t\tif (input.childLabel !== undefined) updates.child_label = input.childLabel;\n\n\t\tif (Object.keys(updates).length > 0) {\n\t\t\tupdates.updated_at = new Date().toISOString();\n\t\t\tawait this.db.updateTable(\"_emdash_relations\").set(updates).where(\"id\", \"=\", id).execute();\n\t\t}\n\n\t\treturn this.findById(id);\n\t}\n\n\t/**\n\t * Delete one relation row. When it is the *last* translation of its group,\n\t * purge edges referencing that group (application-layer cascade — group\n\t * linking precludes a SQL FK). Mirrors `TaxonomyRepository.delete`.\n\t */\n\tasync delete(id: string): Promise<boolean> {\n\t\tconst relation = await this.findById(id);\n\t\tif (!relation) return false;\n\n\t\tconst siblings = await this.db\n\t\t\t.selectFrom(\"_emdash_relations\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"translation_group\", \"=\", relation.translationGroup)\n\t\t\t.where(\"id\", \"!=\", id)\n\t\t\t.execute();\n\t\tif (siblings.length === 0) {\n\t\t\tawait this.db\n\t\t\t\t.deleteFrom(\"_emdash_content_references\")\n\t\t\t\t.where(\"relation_group\", \"=\", relation.translationGroup)\n\t\t\t\t.execute();\n\t\t}\n\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"_emdash_relations\")\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst();\n\t\treturn (result.numDeletedRows ?? 0n) > 0n;\n\t}\n\n\t/** Normalize a relation id OR group to its translation_group. Returns null\n\t * for an unknown relation (edge methods then no-op, matching\n\t * `TaxonomyRepository.attachToEntry`). */\n\tprivate async resolveRelationGroup(idOrGroup: string): Promise<string | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_relations\")\n\t\t\t.select([\"translation_group\"])\n\t\t\t.where((eb) => eb.or([eb(\"id\", \"=\", idOrGroup), eb(\"translation_group\", \"=\", idOrGroup)]))\n\t\t\t.executeTakeFirst();\n\t\treturn row?.translation_group ?? null;\n\t}\n\n\tprivate rowToReference(row: Selectable<ContentReferenceTable>): ContentReference {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\trelationGroup: row.relation_group,\n\t\t\tparentGroup: row.parent_group,\n\t\t\tchildGroup: row.child_group,\n\t\t\tsortOrder: row.sort_order,\n\t\t};\n\t}\n\n\t/**\n\t * Link `parentGroup → childGroup` under a relation. `relation` is a relation\n\t * id or group. Idempotent (onConflict doNothing against the unique edge).\n\t * `sortOrder` defaults to append: max(sort_order)+1 within (relation, parent).\n\t *\n\t * The default-append MAX→INSERT is not atomic: concurrent appends without an\n\t * explicit `sortOrder` may both read the same max and collide on sort_order,\n\t * and onConflict silently drops the loser. Callers needing strict ordering\n\t * under concurrency should pass `sortOrder` explicitly (or serialize).\n\t */\n\tasync addReference(\n\t\trelation: string,\n\t\tparentGroup: string,\n\t\tchildGroup: string,\n\t\tsortOrder?: number,\n\t): Promise<void> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return;\n\n\t\tlet order = sortOrder;\n\t\tif (order === undefined) {\n\t\t\tconst max = await this.db\n\t\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t\t.select((eb) => eb.fn.max(\"sort_order\").as(\"max\"))\n\t\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t\t.where(\"parent_group\", \"=\", parentGroup)\n\t\t\t\t.executeTakeFirst();\n\t\t\torder = max?.max === null || max?.max === undefined ? 0 : Number(max.max) + 1;\n\t\t}\n\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_content_references\")\n\t\t\t.values({\n\t\t\t\tid: ulid(),\n\t\t\t\trelation_group: relationGroup,\n\t\t\t\tparent_group: parentGroup,\n\t\t\t\tchild_group: childGroup,\n\t\t\t\tsort_order: order,\n\t\t\t\tcreated_at: new Date().toISOString(),\n\t\t\t})\n\t\t\t.onConflict((oc) => oc.doNothing())\n\t\t\t.execute();\n\t}\n\n\t/** Remove one `parentGroup → childGroup` edge under a relation. */\n\tasync removeReference(relation: string, parentGroup: string, childGroup: string): Promise<void> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return;\n\n\t\tawait this.db\n\t\t\t.deleteFrom(\"_emdash_content_references\")\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"parent_group\", \"=\", parentGroup)\n\t\t\t.where(\"child_group\", \"=\", childGroup)\n\t\t\t.execute();\n\t}\n\n\t/** Forward traversal: a parent's children for a relation, ordered. */\n\tasync getChildren(relation: string, parentGroup: string): Promise<ContentReference[]> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return [];\n\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t.selectAll()\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"parent_group\", \"=\", parentGroup)\n\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t.orderBy(\"id\", \"asc\")\n\t\t\t.execute();\n\t\treturn rows.map((row) => this.rowToReference(row));\n\t}\n\n\t/**\n\t * Forward traversal, paginated: one page of a parent's children for a\n\t * relation, ordered by `(sort_order, id)`. Use this on request paths — a\n\t * parent's children are capped but still up to 1000, and an unbounded read\n\t * scales poorly. Returns `{ items, nextCursor? }`; the cursor's order value is\n\t * the row's `sort_order`. Default limit 50, max 100.\n\t */\n\tasync getChildrenPage(\n\t\trelation: string,\n\t\tparentGroup: string,\n\t\toptions: { limit?: number; cursor?: string } = {},\n\t): Promise<FindManyResult<ContentReference>> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return { items: [] };\n\n\t\tconst limit = Math.min(options.limit || 50, 100);\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t.selectAll()\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"parent_group\", \"=\", parentGroup);\n\n\t\tif (options.cursor) {\n\t\t\tconst decoded = decodeCursor(options.cursor);\n\t\t\tconst sortOrder = Number(decoded.orderValue);\n\t\t\t// `decodeCursor` only guarantees `orderValue` is a string; a hand-crafted\n\t\t\t// cursor with a non-numeric order value would coerce to NaN and blow up at\n\t\t\t// the driver bind as a 500. A bad cursor is a client error — surface it as\n\t\t\t// INVALID_CURSOR (400). Server-issued cursors are always numeric here.\n\t\t\tif (!Number.isFinite(sortOrder)) throw new InvalidCursorError(options.cursor);\n\t\t\tquery = query.where((eb) =>\n\t\t\t\teb.or([\n\t\t\t\t\teb(\"sort_order\", \">\", sortOrder),\n\t\t\t\t\teb.and([eb(\"sort_order\", \"=\", sortOrder), eb(\"id\", \">\", decoded.id)]),\n\t\t\t\t]),\n\t\t\t);\n\t\t}\n\n\t\tconst rows = await query\n\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t.orderBy(\"id\", \"asc\")\n\t\t\t.limit(limit + 1)\n\t\t\t.execute();\n\n\t\tconst hasMore = rows.length > limit;\n\t\tconst items = rows.slice(0, limit).map((row) => this.rowToReference(row));\n\t\tconst result: FindManyResult<ContentReference> = { items };\n\t\tconst last = items.at(-1);\n\t\tif (hasMore && last) {\n\t\t\tresult.nextCursor = encodeCursor(String(last.sortOrder), last.id);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/** Backlink traversal: the parents that reference a child for a relation. */\n\tasync getParents(relation: string, childGroup: string): Promise<ContentReference[]> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return [];\n\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t.selectAll()\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"child_group\", \"=\", childGroup)\n\t\t\t.orderBy(\"id\", \"asc\")\n\t\t\t.execute();\n\t\treturn rows.map((row) => this.rowToReference(row));\n\t}\n\n\t/**\n\t * Replace all children of `parentGroup` under a relation with `childGroups`,\n\t * assigning positional sort_order (index in the deduped array). Deletes the\n\t * old set for this (relation, parent) and re-inserts — simple and correct;\n\t * the set is small (one parent's children). Mirrors the intent of\n\t * `TaxonomyRepository.setTermsForEntry`.\n\t *\n\t * A parent references a given child at most once (the unique edge), so\n\t * duplicate `childGroups` are collapsed first-occurrence-wins rather than\n\t * relying on the insert's onConflict to silently drop them. Not wrapped in a\n\t * transaction: a crash between the delete and insert leaves the parent with\n\t * no children — acceptable for a replace-all, since a retry restores state.\n\t *\n\t * Concurrency: two simultaneous replace-all calls for the same (relation,\n\t * parent) can interleave their deletes and inserts and merge into the union of\n\t * both sets (a lost update — neither \"replace\" wins). This is non-corrupting —\n\t * keyset pagination stays totally ordered via the `(sort_order, id)` tiebreak\n\t * even with duplicate sort_orders — and a single client editing one parent's\n\t * children serially never hits it. A D1-portable fix isn't available (no\n\t * multi-statement transactions), so concurrent replace-all on one parent is\n\t * unsupported by design rather than guarded here.\n\t */\n\tasync setChildren(relation: string, parentGroup: string, childGroups: string[]): Promise<void> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return;\n\n\t\tawait this.db\n\t\t\t.deleteFrom(\"_emdash_content_references\")\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"parent_group\", \"=\", parentGroup)\n\t\t\t.execute();\n\n\t\t// Collapse duplicates so positional sort_order has no gaps.\n\t\tconst uniqueChildGroups = [...new Set(childGroups)];\n\t\tif (uniqueChildGroups.length === 0) return;\n\n\t\tconst now = new Date().toISOString();\n\t\tawait this.db\n\t\t\t.insertInto(\"_emdash_content_references\")\n\t\t\t.values(\n\t\t\t\tuniqueChildGroups.map((childGroup, index) => ({\n\t\t\t\t\tid: ulid(),\n\t\t\t\t\trelation_group: relationGroup,\n\t\t\t\t\tparent_group: parentGroup,\n\t\t\t\t\tchild_group: childGroup,\n\t\t\t\t\tsort_order: index,\n\t\t\t\t\tcreated_at: now,\n\t\t\t\t})),\n\t\t\t)\n\t\t\t// Belt-and-suspenders: the DELETE above already cleared this\n\t\t\t// (relation, parent), so no conflict is possible within one call.\n\t\t\t// This is NOT a concurrency guarantee — delete-then-insert is not atomic.\n\t\t\t.onConflict((oc) => oc.doNothing())\n\t\t\t.execute();\n\t}\n\n\t/**\n\t * Backlink traversal, paginated: one page of the parents that reference a\n\t * child for a relation, ordered by `id`. Unlike a parent's children, a\n\t * child's backlinks are *unbounded* — one popular entry can be referenced by\n\t * arbitrarily many parents — so this read must paginate. Returns\n\t * `{ items, nextCursor? }`; the cursor's order value is the row `id`. Default\n\t * limit 50, max 100.\n\t */\n\tasync getParentsPage(\n\t\trelation: string,\n\t\tchildGroup: string,\n\t\toptions: { limit?: number; cursor?: string } = {},\n\t): Promise<FindManyResult<ContentReference>> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return { items: [] };\n\n\t\tconst limit = Math.min(options.limit || 50, 100);\n\n\t\tlet query = this.db\n\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t.selectAll()\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"child_group\", \"=\", childGroup);\n\n\t\tif (options.cursor) {\n\t\t\tconst decoded = decodeCursor(options.cursor);\n\t\t\tquery = query.where(\"id\", \">\", decoded.id);\n\t\t}\n\n\t\tconst rows = await query\n\t\t\t.orderBy(\"id\", \"asc\")\n\t\t\t.limit(limit + 1)\n\t\t\t.execute();\n\n\t\tconst hasMore = rows.length > limit;\n\t\tconst items = rows.slice(0, limit).map((row) => this.rowToReference(row));\n\t\tconst result: FindManyResult<ContentReference> = { items };\n\t\tconst last = items.at(-1);\n\t\tif (hasMore && last) {\n\t\t\tresult.nextCursor = encodeCursor(last.id, last.id);\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Remove every edge where `group` is the parent OR the child — i.e. ensure no\n\t * orphaned reference edges survive when a content entry is deleted. The\n\t * application-layer cascade that group-linking precludes at the SQL level.\n\t * Wiring this into the content-delete path is a later (handler) slice.\n\t * Returns the number of edges removed.\n\t */\n\tasync clearReferencesForGroup(group: string): Promise<number> {\n\t\tconst result = await this.db\n\t\t\t.deleteFrom(\"_emdash_content_references\")\n\t\t\t.where((eb) => eb.or([eb(\"parent_group\", \"=\", group), eb(\"child_group\", \"=\", group)]))\n\t\t\t.executeTakeFirst();\n\t\treturn Number(result.numDeletedRows ?? 0);\n\t}\n\n\t/** Count a parent's children under a relation. */\n\tasync countChildren(relation: string, parentGroup: string): Promise<number> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return 0;\n\t\tconst result = await this.db\n\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t.select((eb) => eb.fn.count(\"id\").as(\"count\"))\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"parent_group\", \"=\", parentGroup)\n\t\t\t.executeTakeFirst();\n\t\treturn Number(result?.count ?? 0);\n\t}\n\n\t/** Count a child's parents (backlinks) under a relation. */\n\tasync countParents(relation: string, childGroup: string): Promise<number> {\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return 0;\n\t\tconst result = await this.db\n\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t.select((eb) => eb.fn.count(\"id\").as(\"count\"))\n\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t.where(\"child_group\", \"=\", childGroup)\n\t\t\t.executeTakeFirst();\n\t\treturn Number(result?.count ?? 0);\n\t}\n\n\t/**\n\t * Batch child-counts for many parents under a relation. Chunks at\n\t * SQL_BATCH_SIZE for D1's bind-parameter limit. Returns parent_group → count\n\t * (parents with no children are absent from the map). Mirrors\n\t * `TaxonomyRepository.countEntriesForTerms`.\n\t */\n\tasync countChildrenForParents(\n\t\trelation: string,\n\t\tparentGroups: string[],\n\t): Promise<Map<string, number>> {\n\t\tconst counts = new Map<string, number>();\n\t\tif (parentGroups.length === 0) return counts;\n\t\tconst relationGroup = await this.resolveRelationGroup(relation);\n\t\tif (!relationGroup) return counts;\n\n\t\tfor (const chunk of chunks(parentGroups, SQL_BATCH_SIZE)) {\n\t\t\tconst rows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_content_references\")\n\t\t\t\t.select([\"parent_group\", (eb) => eb.fn.count(\"id\").as(\"count\")])\n\t\t\t\t.where(\"relation_group\", \"=\", relationGroup)\n\t\t\t\t.where(\"parent_group\", \"in\", chunk)\n\t\t\t\t.groupBy(\"parent_group\")\n\t\t\t\t.execute();\n\t\t\tfor (const row of rows) {\n\t\t\t\tcounts.set(row.parent_group, Number(row.count ?? 0));\n\t\t\t}\n\t\t}\n\t\treturn counts;\n\t}\n\n\tprivate rowToRelation(row: Selectable<RelationTable>): Relation {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tname: row.name,\n\t\t\tparentCollection: row.parent_collection,\n\t\t\tchildCollection: row.child_collection,\n\t\t\tparentLabel: row.parent_label,\n\t\t\tchildLabel: row.child_label,\n\t\t\tlocale: row.locale,\n\t\t\ttranslationGroup: row.translation_group,\n\t\t};\n\t}\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { a as RESERVED_BYLINE_FIELD_SLUGS } from "./types-75KIqzL9.mjs";
2
- import { $ as localeCode, Q as httpUrl, nt as roleLevel } from "./media-BWWWb8kg.mjs";
2
+ import { St as roleLevel, vt as httpUrl, yt as localeCode } from "./media-BHjus86Y.mjs";
3
3
  import { z } from "zod";
4
4
 
5
5
  //#region src/api/schemas/auth.ts
@@ -35,14 +35,14 @@ const authenticationCredential = z.object({
35
35
  }),
36
36
  authenticatorAttachment: z.enum(["platform", "cross-platform"]).optional()
37
37
  });
38
- const signupRequestBody = z.object({ email: z.string().email() }).meta({ id: "SignupRequestBody" });
38
+ const signupRequestBody = z.object({ email: z.email() }).meta({ id: "SignupRequestBody" });
39
39
  const signupCompleteBody = z.object({
40
40
  token: z.string().min(1),
41
41
  credential: registrationCredential$1,
42
42
  name: z.string().optional()
43
43
  }).meta({ id: "SignupCompleteBody" });
44
44
  const inviteCreateBody = z.object({
45
- email: z.string().email(),
45
+ email: z.email(),
46
46
  role: roleLevel.optional()
47
47
  }).meta({ id: "InviteCreateBody" });
48
48
  const inviteRegisterOptionsBody = z.object({
@@ -54,8 +54,8 @@ const inviteCompleteBody = z.object({
54
54
  credential: registrationCredential$1,
55
55
  name: z.string().optional()
56
56
  }).meta({ id: "InviteCompleteBody" });
57
- const magicLinkSendBody = z.object({ email: z.string().email() }).meta({ id: "MagicLinkSendBody" });
58
- const passkeyOptionsBody = z.object({ email: z.string().email().optional() }).meta({ id: "PasskeyOptionsBody" });
57
+ const magicLinkSendBody = z.object({ email: z.email() }).meta({ id: "MagicLinkSendBody" });
58
+ const passkeyOptionsBody = z.object({ email: z.email().optional() }).meta({ id: "PasskeyOptionsBody" });
59
59
  const passkeyVerifyBody = z.object({ credential: authenticationCredential }).meta({ id: "PasskeyVerifyBody" });
60
60
  const passkeyRegisterOptionsBody = z.object({ name: z.string().optional() }).meta({ id: "PasskeyRegisterOptionsBody" });
61
61
  const passkeyRegisterVerifyBody = z.object({
@@ -141,7 +141,7 @@ const setupBody = z.object({
141
141
  includeContent: z.boolean()
142
142
  });
143
143
  const setupAdminBody = z.object({
144
- email: z.string().email(),
144
+ email: z.email(),
145
145
  name: z.string().optional()
146
146
  });
147
147
  const setupAdminVerifyBody = z.object({ credential: registrationCredential });
@@ -306,4 +306,4 @@ const referenceParentsResponseSchema = z.object({
306
306
 
307
307
  //#endregion
308
308
  export { inviteCreateBody as A, wpMediaImportBody as C, wpRewriteUrlsBody as D, wpPrepareBody as E, passkeyRegisterVerifyBody as F, passkeyRenameBody as I, passkeyVerifyBody as L, magicLinkSendBody as M, passkeyOptionsBody as N, authMeActionBody as O, passkeyRegisterOptionsBody as P, signupCompleteBody as R, importProbeBody as S, wpPluginExecuteBody as T, atprotoLoginBody as _, relationDefSchema as a, setupAtprotoAdminBody as b, relationTranslationsSchema as c, bylineFieldCreateBody as d, bylineFieldDefinitionSchema as f, bylineFieldUsageResponseSchema as g, bylineFieldUpdateBody as h, referenceParentsResponseSchema as i, inviteRegisterOptionsBody as j, inviteCompleteBody as k, setReferenceChildrenBody as l, bylineFieldReorderBody as m, entryRefSchema as n, relationListResponseSchema as o, bylineFieldListResponseSchema as p, referenceChildrenResponseSchema as r, relationResponseSchema as s, createRelationBody as t, updateRelationBody as u, setupAdminBody as v, wpPluginAnalyzeBody as w, setupBody as x, setupAdminVerifyBody as y, signupRequestBody as z };
309
- //# sourceMappingURL=relations-BBApI4e2.mjs.map
309
+ //# sourceMappingURL=relations-C4htk5sb.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relations-C4htk5sb.mjs","names":["authenticatorTransport","registrationCredential"],"sources":["../src/api/schemas/auth.ts","../src/api/schemas/import.ts","../src/api/schemas/setup.ts","../src/api/schemas/byline-fields.ts","../src/api/schemas/relations.ts"],"sourcesContent":["import { z } from \"zod\";\n\nimport { roleLevel } from \"./common.js\";\n\n// ---------------------------------------------------------------------------\n// WebAuthn credential schemas (matching @emdash-cms/auth/passkey types)\n// ---------------------------------------------------------------------------\n\nconst authenticatorTransport = z.enum([\"usb\", \"nfc\", \"ble\", \"internal\", \"hybrid\"]);\n\n/** RegistrationResponse — sent by the browser after navigator.credentials.create() */\nconst registrationCredential = z.object({\n\tid: z.string(),\n\trawId: z.string(),\n\ttype: z.literal(\"public-key\"),\n\tresponse: z.object({\n\t\tclientDataJSON: z.string(),\n\t\tattestationObject: z.string(),\n\t\ttransports: z.array(authenticatorTransport).optional(),\n\t}),\n\tauthenticatorAttachment: z.enum([\"platform\", \"cross-platform\"]).optional(),\n});\n\n/** AuthenticationResponse — sent by the browser after navigator.credentials.get() */\nconst authenticationCredential = z.object({\n\tid: z.string(),\n\trawId: z.string(),\n\ttype: z.literal(\"public-key\"),\n\tresponse: z.object({\n\t\tclientDataJSON: z.string(),\n\t\tauthenticatorData: z.string(),\n\t\tsignature: z.string(),\n\t\tuserHandle: z.string().optional(),\n\t}),\n\tauthenticatorAttachment: z.enum([\"platform\", \"cross-platform\"]).optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Auth: Input schemas\n// ---------------------------------------------------------------------------\n\nexport const signupRequestBody = z\n\t.object({\n\t\temail: z.email(),\n\t})\n\t.meta({ id: \"SignupRequestBody\" });\n\nexport const signupCompleteBody = z\n\t.object({\n\t\ttoken: z.string().min(1),\n\t\tcredential: registrationCredential,\n\t\tname: z.string().optional(),\n\t})\n\t.meta({ id: \"SignupCompleteBody\" });\n\nexport const inviteCreateBody = z\n\t.object({\n\t\temail: z.email(),\n\t\trole: roleLevel.optional(),\n\t})\n\t.meta({ id: \"InviteCreateBody\" });\n\nexport const inviteRegisterOptionsBody = z\n\t.object({\n\t\ttoken: z.string().min(1),\n\t\tname: z.string().optional(),\n\t})\n\t.meta({ id: \"InviteRegisterOptionsBody\" });\n\nexport const inviteCompleteBody = z\n\t.object({\n\t\ttoken: z.string().min(1),\n\t\tcredential: registrationCredential,\n\t\tname: z.string().optional(),\n\t})\n\t.meta({ id: \"InviteCompleteBody\" });\n\nexport const magicLinkSendBody = z\n\t.object({\n\t\temail: z.email(),\n\t})\n\t.meta({ id: \"MagicLinkSendBody\" });\n\nexport const passkeyOptionsBody = z\n\t.object({\n\t\temail: z.email().optional(),\n\t})\n\t.meta({ id: \"PasskeyOptionsBody\" });\n\nexport const passkeyVerifyBody = z\n\t.object({\n\t\tcredential: authenticationCredential,\n\t})\n\t.meta({ id: \"PasskeyVerifyBody\" });\n\nexport const passkeyRegisterOptionsBody = z\n\t.object({\n\t\tname: z.string().optional(),\n\t})\n\t.meta({ id: \"PasskeyRegisterOptionsBody\" });\n\nexport const passkeyRegisterVerifyBody = z\n\t.object({\n\t\tcredential: registrationCredential,\n\t\tname: z.string().optional(),\n\t})\n\t.meta({ id: \"PasskeyRegisterVerifyBody\" });\n\nexport const passkeyRenameBody = z\n\t.object({\n\t\tname: z.string().min(1),\n\t})\n\t.meta({ id: \"PasskeyRenameBody\" });\n\nexport const authMeActionBody = z\n\t.object({\n\t\taction: z.string().min(1),\n\t})\n\t.meta({ id: \"AuthMeActionBody\" });\n","import { z } from \"zod\";\n\nimport { httpUrl } from \"./common.js\";\n\n// ---------------------------------------------------------------------------\n// Import\n// ---------------------------------------------------------------------------\n\nexport const importProbeBody = z.object({\n\turl: httpUrl,\n});\n\nexport const wpPluginAnalyzeBody = z.object({\n\turl: httpUrl,\n\ttoken: z.string().min(1),\n});\n\nexport const wpPluginExecuteBody = z.object({\n\turl: httpUrl,\n\ttoken: z.string().min(1),\n\tconfig: z.record(z.string(), z.unknown()),\n\t// --- Chunked mode (issue #475). Absent phase = single-shot legacy run. ---\n\t// The admin drives the import in a loop of bounded requests; cross-chunk\n\t// state travels with the client so the server stays stateless.\n\tphase: z.enum([\"content\", \"comments\", \"finalize\"]).optional(),\n\t/** Position within the phase: post-type index + WP page (content), page (comments) */\n\tcursor: z\n\t\t.object({\n\t\t\tpostTypeIndex: z.number().int().min(0).default(0),\n\t\t\tpage: z.number().int().min(1).default(1),\n\t\t})\n\t\t.optional(),\n\t/** WP post ID -> created EmDash item, accumulated across content chunks */\n\tidMap: z\n\t\t.record(z.string(), z.object({ id: z.string().min(1), collection: z.string().min(1) }))\n\t\t.optional(),\n\t/** Source translation group -> EmDash item ID, accumulated across content chunks */\n\ttranslationGroups: z.record(z.string(), z.string().min(1)).optional(),\n\t/** WP comment ID -> EmDash root comment ID, accumulated across comment chunks */\n\tcommentRoots: z.record(z.string(), z.string().min(1)).optional(),\n});\n\nexport const wpPrepareBody = z.object({\n\tpostTypes: z.array(\n\t\tz.object({\n\t\t\tname: z.string().min(1),\n\t\t\tcollection: z.string().min(1),\n\t\t\tfields: z\n\t\t\t\t.array(\n\t\t\t\t\tz.object({\n\t\t\t\t\t\tslug: z.string().min(1),\n\t\t\t\t\t\tlabel: z.string().min(1),\n\t\t\t\t\t\ttype: z.string().min(1),\n\t\t\t\t\t\trequired: z.boolean(),\n\t\t\t\t\t\tsearchable: z.boolean().optional(),\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\t.optional(),\n\t\t}),\n\t),\n});\n\nexport const wpMediaImportBody = z.object({\n\tattachments: z.array(z.record(z.string(), z.unknown())),\n\tstream: z.boolean().optional(),\n});\n\nexport const wpRewriteUrlsBody = z.object({\n\turlMap: z.record(z.string(), z.string()),\n\tcollections: z.array(z.string()).optional(),\n});\n","import { z } from \"zod\";\n\n// ---------------------------------------------------------------------------\n// Setup\n// ---------------------------------------------------------------------------\n\n/** Registration credential — duplicated reference for setup flow.\n * The canonical definition lives in auth.ts but setup needs it independently\n * because setup runs before auth is configured. */\nconst authenticatorTransport = z.enum([\"usb\", \"nfc\", \"ble\", \"internal\", \"hybrid\"]);\n\nconst registrationCredential = z.object({\n\tid: z.string(),\n\trawId: z.string(),\n\ttype: z.literal(\"public-key\"),\n\tresponse: z.object({\n\t\tclientDataJSON: z.string(),\n\t\tattestationObject: z.string(),\n\t\ttransports: z.array(authenticatorTransport).optional(),\n\t}),\n\tauthenticatorAttachment: z.enum([\"platform\", \"cross-platform\"]).optional(),\n});\n\nexport const setupBody = z.object({\n\ttitle: z.string().min(1),\n\ttagline: z.string().optional(),\n\tincludeContent: z.boolean(),\n});\n\nexport const setupAdminBody = z.object({\n\temail: z.email(),\n\tname: z.string().optional(),\n});\n\nexport const setupAdminVerifyBody = z.object({\n\tcredential: registrationCredential,\n});\n\nexport const atprotoLoginBody = z.object({\n\thandle: z.string().trim().min(1),\n});\n\nexport const setupAtprotoAdminBody = z.object({\n\thandle: z.string().trim().min(1),\n});\n","/**\n * Zod schemas for the byline-fields admin API (Discussion #1174, Phase 4).\n *\n * Reserved-slug + identifier validation runs at the zod layer so the\n * route returns a clean 400 (`VALIDATION_ERROR` from `parseBody`) rather\n * than bubbling a registry-level `BylineSchemaError` (\"RESERVED_SLUG\" /\n * \"INVALID_SLUG\"). The registry repeats the same checks for non-HTTP\n * callers (seeds, scripts) — see `BylineSchemaRegistry.validateSlug`.\n *\n * Field types are constrained to the v1 subset declared in\n * `BYLINE_FIELD_TYPES`. Adding a type to the union there will require a\n * corresponding update to this enum.\n */\n\nimport { z } from \"zod\";\n\nimport { BYLINE_FIELD_TYPES, RESERVED_BYLINE_FIELD_SLUGS } from \"../../schema/types.js\";\n\n/**\n * Slug pattern for byline field definitions — matches the identifier rule\n * used by `validateIdentifier` (and `slugPattern` in `common.ts`).\n * Lowercase letters, digits, and underscores; must start with a letter.\n */\nconst bylineFieldSlugPattern = /^[a-z][a-z0-9_]*$/;\n\n/** Hard cap on a slug — mirrors `BylineSchemaRegistry.MAX_SLUG_LENGTH`. */\nconst MAX_SLUG_LENGTH = 63;\n/** Hard cap on a label — mirrors `BylineSchemaRegistry.MAX_LABEL_LENGTH`. */\nconst MAX_LABEL_LENGTH = 200;\n/** Hard cap on a select field's `options` list. */\nconst MAX_SELECT_OPTIONS = 200;\n\nconst RESERVED_SET: ReadonlySet<string> = new Set(RESERVED_BYLINE_FIELD_SLUGS);\n\n// Enumerate the v1 byline field types explicitly so zod gets the exact\n// literal union for `z.infer<>`. Mirrors `BYLINE_FIELD_TYPES`; CI's\n// type-checker catches drift via the satisfies/import below.\nconst bylineFieldTypeValues = z.enum([\"string\", \"text\", \"url\", \"boolean\", \"select\"]);\n// Compile-time guard: a drift here trips the satisfies check.\ntype _BylineFieldTypeDriftCheck =\n\t(typeof BYLINE_FIELD_TYPES)[number] extends z.infer<typeof bylineFieldTypeValues>\n\t\t? z.infer<typeof bylineFieldTypeValues> extends (typeof BYLINE_FIELD_TYPES)[number]\n\t\t\t? true\n\t\t\t: never\n\t\t: never;\nconst _bylineFieldTypeDriftCheck: _BylineFieldTypeDriftCheck = true;\nvoid _bylineFieldTypeDriftCheck;\n\n/**\n * Validation payload for a byline custom field. v1 only exposes\n * `options` (used by `select`-type fields). Empty/duplicate options are\n * rejected at the registry layer; the zod layer only enforces shape and\n * caps. Future field types may add keys here.\n */\nconst bylineFieldValidationSchema = z\n\t.object({\n\t\toptions: z\n\t\t\t.array(z.string().min(1))\n\t\t\t.min(1, \"select options must contain at least one entry\")\n\t\t\t.max(MAX_SELECT_OPTIONS, `select options cannot exceed ${MAX_SELECT_OPTIONS} entries`)\n\t\t\t.optional(),\n\t})\n\t.strict()\n\t.nullable();\n\n/**\n * Slug validation chain shared by create + reorder bodies. Centralised so\n * the reserved-slug message and pattern are identical everywhere.\n */\nconst bylineFieldSlug = z\n\t.string()\n\t.min(1, \"Byline field slug is required\")\n\t.max(MAX_SLUG_LENGTH, `Byline field slug must be ${MAX_SLUG_LENGTH} characters or less`)\n\t.regex(\n\t\tbylineFieldSlugPattern,\n\t\t\"Byline field slug must contain only lowercase letters, digits, and underscores, and start with a letter\",\n\t)\n\t.refine((slug) => !RESERVED_SET.has(slug), {\n\t\t// Surface the offending slug in the validation issue path-message\n\t\t// for easier debugging from the admin UI's error toast.\n\t\tmessage: \"Byline field slug is reserved\",\n\t});\n\nconst bylineFieldLabel = z\n\t.string()\n\t.min(1, \"Byline field label is required\")\n\t.max(MAX_LABEL_LENGTH, `Byline field label must be ${MAX_LABEL_LENGTH} characters or less`);\n\n// ---------------------------------------------------------------------------\n// Request bodies\n// ---------------------------------------------------------------------------\n\nexport const bylineFieldCreateBody = z\n\t.object({\n\t\tslug: bylineFieldSlug,\n\t\tlabel: bylineFieldLabel,\n\t\ttype: bylineFieldTypeValues,\n\t\trequired: z.boolean().optional(),\n\t\t/**\n\t\t * Whether values are stored per-locale (translatable, default) or\n\t\t * shared across the translation group. See `BylineFieldDefinition`.\n\t\t */\n\t\ttranslatable: z.boolean().optional(),\n\t\tvalidation: bylineFieldValidationSchema.optional(),\n\t\tsortOrder: z.number().int().min(0).optional(),\n\t})\n\t.strict()\n\t.meta({ id: \"BylineFieldCreateBody\" });\n\n/**\n * Update body. `slug` and `type` are intentionally absent — both are\n * immutable post-create (changing them would invalidate stored values).\n * `translatable` flips are gated at the registry layer when value rows\n * exist (`TRANSLATABLE_LOCKED`).\n */\nexport const bylineFieldUpdateBody = z\n\t.object({\n\t\tlabel: bylineFieldLabel.optional(),\n\t\trequired: z.boolean().optional(),\n\t\ttranslatable: z.boolean().optional(),\n\t\tvalidation: bylineFieldValidationSchema.optional(),\n\t\tsortOrder: z.number().int().min(0).optional(),\n\t})\n\t.strict()\n\t.meta({ id: \"BylineFieldUpdateBody\" });\n\nexport const bylineFieldReorderBody = z\n\t.object({\n\t\t/**\n\t\t * Exact set of currently registered slugs in the desired order.\n\t\t * The registry rejects any drift (`REORDER_MISMATCH`); the zod\n\t\t * layer enforces slug shape only. An empty array is permitted —\n\t\t * `reorderFields([])` is a valid no-op when zero fields are\n\t\t * registered (registry contract). Rejecting empty here would\n\t\t * produce a spurious 400 for an admin UI that submits a reorder\n\t\t * after deleting the last field.\n\t\t */\n\t\tslugs: z.array(bylineFieldSlug),\n\t})\n\t.strict()\n\t.meta({ id: \"BylineFieldReorderBody\" });\n\n// ---------------------------------------------------------------------------\n// Response shapes\n// ---------------------------------------------------------------------------\n\nexport const bylineFieldDefinitionSchema = z\n\t.object({\n\t\tid: z.string(),\n\t\tslug: z.string(),\n\t\tlabel: z.string(),\n\t\ttype: bylineFieldTypeValues,\n\t\trequired: z.boolean(),\n\t\ttranslatable: z.boolean(),\n\t\tvalidation: z\n\t\t\t.object({\n\t\t\t\toptions: z.array(z.string()).optional(),\n\t\t\t})\n\t\t\t.nullable(),\n\t\tsortOrder: z.number().int(),\n\t\tcreatedAt: z.string(),\n\t\tupdatedAt: z.string(),\n\t})\n\t.meta({ id: \"BylineFieldDefinition\" });\n\nexport const bylineFieldListResponseSchema = z\n\t.object({\n\t\titems: z.array(bylineFieldDefinitionSchema),\n\t})\n\t.meta({ id: \"BylineFieldListResponse\" });\n\n/**\n * Response shape for `GET /api/admin/byline-fields/[slug]/usage`.\n *\n * `translatableValueCount` counts rows in `_emdash_byline_field_values`.\n * `groupValueCount` counts rows in `_emdash_byline_field_group_values`.\n * `totalAffectedRows` is the sum — what the destructive-delete confirm\n * dialog surfaces. Both individual counts are exposed for diagnostic\n * value (e.g. inconsistency with the field's current `translatable`\n * flag would show non-zero on the \"wrong\" side).\n */\nexport const bylineFieldUsageResponseSchema = z\n\t.object({\n\t\ttranslatableValueCount: z.number().int().nonnegative(),\n\t\tgroupValueCount: z.number().int().nonnegative(),\n\t\ttotalAffectedRows: z.number().int().nonnegative(),\n\t})\n\t.meta({ id: \"BylineFieldUsageResponse\" });\n","import { z } from \"zod\";\n\nimport { localeCode } from \"./common.js\";\n\nconst slugPattern = /^[a-z][a-z0-9_]*$/;\nconst collectionSlug = z\n\t.string()\n\t.min(1)\n\t.max(63)\n\t.regex(slugPattern, \"Invalid collection slug format\");\n\nexport const createRelationBody = z\n\t.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(63)\n\t\t\t.regex(slugPattern, \"Name must be lowercase alphanumeric with underscores\"),\n\t\tparentCollection: collectionSlug.optional(),\n\t\tchildCollection: collectionSlug.optional(),\n\t\tparentLabel: z.string().min(1).max(200),\n\t\tchildLabel: z.string().min(1).max(200),\n\t\tlocale: localeCode.optional(),\n\t\ttranslationOf: z.string().min(1).optional(),\n\t})\n\t// A translation inherits its structural fields (name, parentCollection,\n\t// childCollection) from the source relation, so the handler ignores any\n\t// collections supplied alongside `translationOf`. Require them only when\n\t// minting a base relation, so callers aren't forced to pass discarded values.\n\t.refine(\n\t\t(body) =>\n\t\t\tbody.translationOf !== undefined ||\n\t\t\t(body.parentCollection !== undefined && body.childCollection !== undefined),\n\t\t{ message: \"parentCollection and childCollection are required unless translationOf is set\" },\n\t)\n\t.meta({ id: \"CreateRelationBody\" });\n\nexport const updateRelationBody = z\n\t.object({\n\t\tparentLabel: z.string().min(1).max(200).optional(),\n\t\tchildLabel: z.string().min(1).max(200).optional(),\n\t})\n\t// Reject empty payloads: an update touching no field is a client mistake, not\n\t// a successful no-op. Without this, `{}` validates and the handler returns 200\n\t// with the unchanged row, so a typo'd payload looks like it landed.\n\t.refine((body) => body.parentLabel !== undefined || body.childLabel !== undefined, {\n\t\tmessage: \"At least one of parentLabel or childLabel is required\",\n\t})\n\t.meta({ id: \"UpdateRelationBody\" });\n\nexport const setReferenceChildrenBody = z\n\t.object({ childIds: z.array(z.string().min(1)).max(1000) })\n\t.meta({ id: \"SetReferenceChildrenBody\" });\n\nexport const relationDefSchema = z\n\t.object({\n\t\tid: z.string(),\n\t\tname: z.string(),\n\t\tparentCollection: z.string(),\n\t\tchildCollection: z.string(),\n\t\tparentLabel: z.string(),\n\t\tchildLabel: z.string(),\n\t\tlocale: z.string(),\n\t\ttranslationGroup: z.string(),\n\t})\n\t.meta({ id: \"RelationDef\" });\n\nexport const relationListResponseSchema = z\n\t.object({ relations: z.array(relationDefSchema) })\n\t.meta({ id: \"RelationListResponse\" });\n\nexport const relationResponseSchema = z\n\t.object({ relation: relationDefSchema })\n\t.meta({ id: \"RelationResponse\" });\n\nexport const relationTranslationsSchema = z\n\t.object({\n\t\ttranslationGroup: z.string(),\n\t\ttranslations: z.array(\n\t\t\tz.object({\n\t\t\t\tid: z.string(),\n\t\t\t\tname: z.string(),\n\t\t\t\tlocale: z.string(),\n\t\t\t\tparentLabel: z.string(),\n\t\t\t\tchildLabel: z.string(),\n\t\t\t}),\n\t\t),\n\t})\n\t.meta({ id: \"RelationTranslations\" });\n\nexport const entryRefSchema = z\n\t.object({\n\t\tid: z.string(),\n\t\tslug: z.string().nullable(),\n\t\tcollection: z.string(),\n\t\t// The actual locale of the resolved variant. When no variant matches the\n\t\t// requesting entry's locale, the ref falls back to another locale's row;\n\t\t// this field makes that substitution explicit instead of silently\n\t\t// presenting a wrong-locale entry under the requested context.\n\t\tlocale: z.string().nullable(),\n\t\tsortOrder: z.number().int().optional(),\n\t})\n\t.meta({ id: \"ReferenceEntryRef\" });\n\nexport const referenceChildrenResponseSchema = z\n\t.object({ children: z.array(entryRefSchema), nextCursor: z.string().optional() })\n\t.meta({ id: \"ReferenceChildrenResponse\" });\n\nexport const referenceParentsResponseSchema = z\n\t.object({ parents: z.array(entryRefSchema), nextCursor: z.string().optional() })\n\t.meta({ id: \"ReferenceParentsResponse\" });\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { a as resolveConfiguredLocale } from "./config-C3EBstHi.mjs";
2
- import { t as ContentRepository } from "./content-ZHCc1ngN.mjs";
2
+ import { t as ContentRepository } from "./content-DjvM0T92.mjs";
3
3
  import { i as InvalidCursorError } from "./types-CQPWhPxE.mjs";
4
- import { t as RelationRepository } from "./relation-BnHB0T4J.mjs";
5
- import { n as SchemaRegistry } from "./registry-Cqa2I749.mjs";
4
+ import { t as RelationRepository } from "./relation-04DfKRTO.mjs";
5
+ import { n as SchemaRegistry } from "./registry-AZAsRsor.mjs";
6
6
 
7
7
  //#region src/api/handlers/relations.ts
8
8
  /** Map an edge-read failure: a bad pagination cursor is a 400 client error,
@@ -400,4 +400,4 @@ async function handleReferenceParentsGet(db, collection, entryId, relation, page
400
400
 
401
401
  //#endregion
402
402
  export { handleRelationDelete as a, handleRelationTranslations as c, handleRelationCreate as i, handleRelationUpdate as l, handleReferenceChildrenSet as n, handleRelationGet as o, handleReferenceParentsGet as r, handleRelationList as s, handleReferenceChildrenGet as t };
403
- //# sourceMappingURL=relations-D5sM5fW0.mjs.map
403
+ //# sourceMappingURL=relations-CcFwDJdJ.mjs.map